PageSourceSearch

https://magalia.wiki/js/symbolon-research.js?v=bba721f0c5

js magalia.wiki collected 2026-10-07 16:32:31 UTC 9,679 bytes, 114 lines download raw bytes

1/* Shared browser/CLI research audit. Integrity is not authorship or historical truth. */
2(function (g) {
3'use strict';
4const I = g.SymbolonInvestigation;
5const hash = value => I.hash(JSON.stringify(value));
6const sourceText = record => typeof record.raw.fulltext === 'string' ? record.raw.fulltext : typeof record.raw.text === 'string' ? record.raw.text : '';
7const ref = id => ({'@id': 'urn:symbolon:' + encodeURIComponent(id)});
8function requireThat(ok, reason) { if (!ok) throw new Error(reason); }
9
10async function audit(state, reviews = state.reviews || []) {
11  state = await I.verify(state);
12  requireThat(Array.isArray(reviews), 'Reviews must be an array');
13  const findings = [], add = (code, id, detail) => findings.push({code, id, detail});
14  const ids = [...state.revisions, ...state.sourceSets, ...state.artifacts, ...state.receipts, ...(state.hypotheses || []), ...(state.plans || [])].map(x => x.id);
15  requireThat(new Set(ids).size === ids.length, 'IDs collide across investigation entity types');
16  const exists = (list, id) => id == null || list.some(x => x.id === id);
17  state.revisions.forEach((r, n) => {
18    requireThat(r.parent === (n ? state.revisions[n - 1].id : null), 'Broken ordered revision chain');
19    requireThat(exists(state.sourceSets, r.sourceSet) && exists(state.artifacts, r.artifact), 'Broken revision reference');
20  });
21  function parents(list) {
22    const visited = new Set();
23    list.forEach(x => { requireThat(x.parent == null || visited.has(x.parent), 'Parent must precede child: ' + x.id); visited.add(x.id); });
24  }
25  parents(state.sourceSets); parents(state.artifacts);
26  const evidenceIDs = new Map();
27  for (const set of state.sourceSets) for (const record of set.records) {
28    requireThat(typeof record.id === 'string' && !ids.includes(record.id), 'Invalid evidence ID');
29    const fingerprint = await hash(record);
30    requireThat(!evidenceIDs.has(record.id) || evidenceIDs.get(record.id) === fingerprint, 'Conflicting evidence ID');
31    evidenceIDs.set(record.id, fingerprint);
32  }
33  const knownReviewArtifacts = new Set();
34  for (const review of reviews) {
35    const a = state.artifacts.find(x => x.id === review.artifactId);
36    requireThat(a && review.artifactSha256 === await hash(a), 'Review is stale or refers to an unknown artifact');
37    requireThat(typeof review.reviewer === 'string' && review.reviewer.trim() && typeof review.reviewedAt === 'string' && Number.isFinite(Date.parse(review.reviewedAt)), 'Reviewer and review date required');
38    requireThat(!knownReviewArtifacts.has(a.id), 'Duplicate artifact review'); knownReviewArtifacts.add(a.id);
39    requireThat(review.scope === 'all-source-claims' && Array.isArray(review.claims), 'Review must declare its claim inventory scope');
40    requireThat(typeof review.coverageNote === 'string' && review.coverageNote.trim(), 'Reviewer must explain claim coverage');
41    const records = state.sourceSets.find(s => s.id === a.sourceSet)?.records || [];
42    const seen = new Set();
43    for (const c of review.claims) {
44      requireThat(typeof c.id === 'string' && c.id.trim() && !seen.has(c.id), 'Duplicate or missing claim ID'); seen.add(c.id);
45      requireThat(typeof c.text === 'string' && c.text.trim() && a.content.includes(c.text), 'Claim text must occur in the reviewed artifact');
46      requireThat(['supported', 'contradicted', 'unresolved', 'interpretation'].includes(c.status), 'Invalid claim review status');
47      requireThat(typeof c.reason === 'string' && c.reason.trim() && Array.isArray(c.evidence), 'Review reason and evidence array required');
48      for (const e of c.evidence) {
49        const source = records.find(r => r.id === e.recordId);
50        requireThat(source, 'Review cites evidence outside the artifact source set');
51        requireThat(typeof e.quote === 'string' && e.quote.trim() && sourceText(source).includes(e.quote), 'Evidence quote is absent from the saved source text');
52      }
53      if (c.status === 'supported') requireThat(c.evidence.length > 0, 'Supported claim needs saved quoted evidence');
54      if (c.status !== 'supported') add('claim-' + c.status, c.id, c.reason);
55    }
56    if (!review.claims.length) add('empty-claim-review', a.id, 'No source claims inventoried; this cannot establish support coverage');
57  }
58  for (const set of state.sourceSets) for (const r of set.records) {
59    if (r.raw.identity_status || r.anchor.identityStatus) add('source-identity-unresolved', r.id, r.raw.identity_status || r.anchor.identityStatus);
60    if (!r.anchor.sourceVersion) add('source-version-unknown', r.id, 'Saved record hash is not an external edition/version identifier');
61    if (!r.anchor.rightsStatus || r.anchor.rightsStatus === 'unknown') add('rights-unresolved', r.id, 'Private package does not grant redistribution permission');
62    if (r.anchor.requestedLocus && r.anchor.requestedLocus !== r.anchor.resolvedLocus) add('locus-unresolved', r.id, 'Requested and resolved locus differ');
63    if (!r.anchor.resolvedLocus) add('locus-unrecorded', r.id, 'Exact source location is not recorded');
64  }
65  for (const a of state.artifacts) {
66    const rows = I.rows(state, a.sourceSet), gate = globalThis.SymbolonEvidence.gate(a.content, rows);
67    if (!gate.grounded) add('citation-resolution-incomplete', a.id, 'Missing, ambiguous or unresolved citations; not a claim-support test');
68    if (!knownReviewArtifacts.has(a.id)) add('support-unreviewed', a.id, 'No hash-bound human claim review');
69    if (a.status !== 'complete') add('artifact-incomplete', a.id, String(a.status));
70  }
71  for (const r of state.receipts) {
72    if (!['tool', 'model'].includes(r.kind)) continue;
73    if (r.result?.status !== 'complete') add('operation-incomplete', r.id, r.result?.status || 'unknown');
74    const raw = r.result?.raw || {}, provenance = raw.provenance || r.result?.provenance;
75    if (raw.pilot || r.result?.pilot || provenance?.kind === 'pilot') add('pilot-output', r.id, 'Pilot output cannot qualify the upstream specialist');
76    if (r.kind === 'model' && !r.identity) add('model-identity-missing', r.id, 'No conversational model identity recorded');
77    if (['restore_text', 'attribute_text'].includes(r.name) && !provenance) add('specialist-provenance-missing', r.id, 'No specialist identity recorded');
78  }
79  if (!state.artifacts.length) add('no-artifact', state.id, 'There is no research artifact to review');
80  return {integrity: 'PASS', reviewReadiness: findings.length ? 'INCOMPLETE' : 'RECORDED_CHECKS_COMPLETE', findings,
81    limits: ['Checksums detect changed bytes; this unsigned package does not authenticate authorship.', 'Human claim coverage and support are self-declared, not automatically certified.', 'No model accuracy, historical truth, licence clearance or publication readiness is certified.']};
82}
83async function provenance(state, reviews) {
84  const graph = [];
85  for (const receipt of state.receipts) graph.push({...ref(receipt.id), '@type': 'prov:Activity', 'sym:receiptHash': await hash(receipt), 'sym:kind': receipt.kind});
86  for (const set of state.sourceSets) for (const r of set.records) graph.push({...ref(r.id), '@type': 'prov:Entity', 'sym:citationKey': r.citationKey, 'sym:contentHash': r.anchor.sourceContentHash,
87    ...(r.receiptId ? {'prov:wasGeneratedBy': ref(r.receiptId)} : {})});
88  for (const a of state.artifacts) graph.push({...ref(a.id), '@type': 'prov:Entity', 'sym:contentHash': await hash(a),
89    'prov:wasDerivedFrom': (state.sourceSets.find(s => s.id === a.sourceSet)?.records || []).map(r => ref(r.id))});
90  for (const [n, review] of reviews.entries()) {
91    const reviewer = 'reviewer-' + await hash(review.reviewer), activity = state.id + '-review-' + n;
92    graph.push({...ref(reviewer), '@type': 'prov:Agent', 'sym:selfDeclaredName': review.reviewer});
93    graph.push({...ref(activity), '@type': 'prov:Activity', 'prov:used': ref(review.artifactId), 'prov:wasAssociatedWith': ref(reviewer), 'sym:reviewHash': await hash(review)});
94  }
95  return {'@context': {prov: 'http://www.w3.org/ns/prov#', sym: 'https://magalia.wiki/ns/symbolon#'}, '@graph': graph};
96}
97async function bundle(state, reviews = state.reviews || []) {
98  state = await I.verify(state); reviews = I.copy(reviews);
99  if (state.reviews?.length) requireThat(await hash(state.reviews) === await hash(reviews), 'Embedded and supplied reviews differ');
100  const payload = {investigation: state, reviews, audit: await audit(state, reviews), provenance: await provenance(state, reviews)};
101  return {format: 'symbolon-private-research-package-v1', privateOnly: true, payload,
102    sha256: Object.fromEntries(await Promise.all(Object.entries(payload).map(async ([k, v]) => [k, await hash(v)])))};
103}
104async function verifyPackage(pkg) {
105  requireThat(pkg.format === 'symbolon-private-research-package-v1' && pkg.privateOnly === true, 'Unsupported private package');
106  const keys = ['audit', 'investigation', 'provenance', 'reviews'];
107  requireThat(pkg.payload && pkg.sha256 && JSON.stringify(Object.keys(pkg.payload).sort()) === JSON.stringify(keys) && JSON.stringify(Object.keys(pkg.sha256).sort()) === JSON.stringify(keys), 'Incomplete package manifest');
108  for (const k of keys) requireThat(await hash(pkg.payload[k]) === pkg.sha256[k], 'Package checksum mismatch: ' + k);
109  const current = await bundle(pkg.payload.investigation, pkg.payload.reviews);
110  requireThat(await hash(current.payload.audit) === pkg.sha256.audit && await hash(current.payload.provenance) === pkg.sha256.provenance, 'Audit/provenance does not correspond to the saved investigation');
111  return current.payload.audit;
112}
113g.SymbolonResearch = {audit, bundle, verifyPackage, hash, sourceText};
114})(typeof window !== 'undefined' ? window : globalThis);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.