instar 1.3.1077 → 1.3.1079

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -238,12 +238,33 @@ export interface CoverageReport {
238
238
  * a confident zero.
239
239
  */
240
240
  export interface GuardTreeProvenance {
241
- /** The directory refs were resolved against. */
241
+ /** The directory refs were resolved against, or the packed-index sentinel. */
242
242
  projectDir: string;
243
+ /** The configured directory, retained when a packed index supersedes a stale local tree. */
244
+ configuredProjectDir: string;
243
245
  /** Whether that directory actually contains an analyzable instar source tree. */
244
246
  analyzable: boolean;
245
247
  /** The markers probed, so a reader can see WHY it was judged (un)analyzable. */
246
248
  markersFound: string[];
249
+ /** The independently checkable basis used to select this guard evidence. */
250
+ basis: 'executing-source-tree' | 'source-tree-index-match' | 'packed-source-index-match' | 'configured-tree-unverified' | 'not-probed';
251
+ /** True only when the tree/index is tied to the code and constitution being read. */
252
+ freshnessVerified: boolean;
253
+ /** Plain-English explanation of what established (or failed to establish) freshness. */
254
+ freshnessReason: string;
255
+ /** sha256 of the packed source index, when that is the basis. */
256
+ sourceIndexSha256: string | null;
257
+ /** Registry sha the packed index was generated against. */
258
+ registrySha256: string | null;
259
+ /** Package version the packed index was generated for. */
260
+ packageVersion: string | null;
261
+ }
262
+ export interface GuardTreeIndex {
263
+ schemaVersion: 1;
264
+ generatedFrom: 'source-tree';
265
+ registrySha256: string;
266
+ packageVersion: string;
267
+ guards: VerifiedGuard[];
247
268
  }
248
269
  export interface AuditorOptions {
249
270
  /** Path to docs/STANDARDS-REGISTRY.md. */
@@ -342,6 +363,19 @@ export declare function stripComments(code: string): string;
342
363
  * non-resolving ref, which is the conservative direction.
343
364
  */
344
365
  export declare function containedRefPath(projectDir: string, ref: string): string | null;
366
+ /**
367
+ * Build the deterministic evidence index shipped beside the packed constitution.
368
+ *
369
+ * This runs in the real source checkout during `npm run build`, where the source tree
370
+ * is unambiguous. Runtime installs need not guess which old checkout happens to sit in
371
+ * `projectDir`; they verify and consume this same-build index instead.
372
+ */
373
+ export declare function buildGuardTreeIndex(projectDir: string, registryMarkdown: string, packageVersion: string): GuardTreeIndex;
374
+ /**
375
+ * Select a real checkout only when its audit-relevant evidence exactly matches the
376
+ * same-build packed index. Landmark presence alone is deliberately insufficient.
377
+ */
378
+ export declare function findMatchingGuardTree(candidates: string[], expected: GuardTreeIndex, registryMarkdown: string, packageVersion: string): string | null;
345
379
  export declare function computeInputHash(opts: AuditorOptions): string;
346
380
  /**
347
381
  * Derive how much the assessment has EARNED. Extracted as a function on purpose:
@@ -1 +1 @@
1
- {"version":3,"file":"StandardsEnforcementAuditor.d.ts","sourceRoot":"","sources":["../../src/core/StandardsEnforcementAuditor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StandardsEnforcementAuditor.d.ts","sourceRoot":"","sources":["../../src/core/StandardsEnforcementAuditor.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,7 @@
24
24
  import fs from 'node:fs';
25
25
  import path from 'node:path';
26
26
  import crypto from 'node:crypto';
27
+ import { fileURLToPath } from 'node:url';
27
28
  import { parseStandardsRegistryDetailed, runRegistryCanary } from './StandardsRegistryParser.js';
28
29
  import { extractEnforcementRefs, flattenRefs } from './StandardEnforcementExtractor.js';
29
30
  import { earnsVerified, readAuthoredConstitution } from './standardsRegistryPath.js';
@@ -290,6 +291,251 @@ function verifyRefs(refs, projectDir, routeTable, symbolIndex) {
290
291
  return g;
291
292
  });
292
293
  }
294
+ function guardKey(guard) {
295
+ return `${guard.kind}\u0000${guard.ref}`;
296
+ }
297
+ /**
298
+ * Build the deterministic evidence index shipped beside the packed constitution.
299
+ *
300
+ * This runs in the real source checkout during `npm run build`, where the source tree
301
+ * is unambiguous. Runtime installs need not guess which old checkout happens to sit in
302
+ * `projectDir`; they verify and consume this same-build index instead.
303
+ */
304
+ export function buildGuardTreeIndex(projectDir, registryMarkdown, packageVersion) {
305
+ const { articles } = parseStandardsRegistryDetailed(registryMarkdown);
306
+ const extracted = articles.map((article) => extractEnforcementRefs(article));
307
+ const refsByKey = new Map();
308
+ const wantedMarkers = new Set();
309
+ for (const refs of extracted) {
310
+ for (const ref of flattenRefs(refs)) {
311
+ refsByKey.set(`${ref.kind}\u0000${ref.ref}`, ref);
312
+ if (ref.kind === 'marker')
313
+ wantedMarkers.add(ref.ref);
314
+ }
315
+ }
316
+ const routeTable = loadRouteTable(projectDir);
317
+ const symbolIndex = buildSymbolIndex(projectDir, wantedMarkers);
318
+ const guards = verifyRefs([...refsByKey.values()].sort((a, b) => guardKey(a).localeCompare(guardKey(b))), projectDir, routeTable, symbolIndex);
319
+ return {
320
+ schemaVersion: 1,
321
+ generatedFrom: 'source-tree',
322
+ registrySha256: crypto.createHash('sha256').update(registryMarkdown).digest('hex'),
323
+ packageVersion,
324
+ guards,
325
+ };
326
+ }
327
+ function executingPackageRoot() {
328
+ try {
329
+ return fileURLToPath(new URL('../../', import.meta.url));
330
+ }
331
+ catch {
332
+ // @silent-fallback-ok: this is one candidate identity only. The packed-index
333
+ // resolver below remains authoritative for published installs and returns a
334
+ // surfaced, untrustworthy provenance when it cannot verify its own artifacts.
335
+ return null;
336
+ }
337
+ }
338
+ function sameDirectory(a, b) {
339
+ try {
340
+ return fs.realpathSync(a) === fs.realpathSync(b);
341
+ }
342
+ catch {
343
+ // @silent-fallback-ok: inability to prove path identity is a negative result,
344
+ // never permission to label a configured tree as current.
345
+ return false;
346
+ }
347
+ }
348
+ function sameGuardIndex(left, right) {
349
+ return JSON.stringify(left) === JSON.stringify(right);
350
+ }
351
+ /**
352
+ * Select a real checkout only when its audit-relevant evidence exactly matches the
353
+ * same-build packed index. Landmark presence alone is deliberately insufficient.
354
+ */
355
+ export function findMatchingGuardTree(candidates, expected, registryMarkdown, packageVersion) {
356
+ const seen = new Set();
357
+ for (const candidate of candidates) {
358
+ let canonical;
359
+ try {
360
+ canonical = fs.realpathSync(candidate);
361
+ }
362
+ catch {
363
+ // @silent-fallback-ok: a missing candidate is not a degraded audit result; it
364
+ // simply cannot prove identity and the next bounded candidate is evaluated.
365
+ continue;
366
+ }
367
+ if (seen.has(canonical))
368
+ continue;
369
+ seen.add(canonical);
370
+ if (!probeGuardTree(canonical).analyzable)
371
+ continue;
372
+ let observed;
373
+ try {
374
+ observed = buildGuardTreeIndex(canonical, registryMarkdown, packageVersion);
375
+ }
376
+ catch {
377
+ // @silent-fallback-ok: one optional candidate that cannot be fully indexed
378
+ // has not established identity. Continue to the next bounded candidate; the
379
+ // packed fallback retains analysis and exposes its own basis.
380
+ continue;
381
+ }
382
+ if (sameGuardIndex(observed, expected))
383
+ return canonical;
384
+ }
385
+ return null;
386
+ }
387
+ function guardTreeCandidates(configuredProjectDir) {
388
+ const candidates = [
389
+ configuredProjectDir,
390
+ path.join(configuredProjectDir, 'repo'),
391
+ process.cwd(),
392
+ ];
393
+ const packageRoot = executingPackageRoot();
394
+ if (packageRoot)
395
+ candidates.push(packageRoot);
396
+ // Agent deployments keep a bounded fleet at `<agents>/<name>` and development
397
+ // agents keep their checkout at `<agent>/repo`. Search only that one known level:
398
+ // no home-directory crawl, no arbitrary project discovery.
399
+ const agentsDir = path.dirname(configuredProjectDir);
400
+ if (path.basename(agentsDir) === 'agents') {
401
+ try {
402
+ for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true }).slice(0, 100)) {
403
+ if (entry.isDirectory())
404
+ candidates.push(path.join(agentsDir, entry.name, 'repo'));
405
+ }
406
+ }
407
+ catch {
408
+ // @silent-fallback-ok: fleet-layout discovery is an optional bounded candidate
409
+ // source. Package-local evidence below retains the capability and reports its basis.
410
+ }
411
+ }
412
+ return candidates;
413
+ }
414
+ function readPackedGuardIndex(registrySha256, runningPackageVersion, configuredProjectDir) {
415
+ let indexPath;
416
+ let metaPath;
417
+ try {
418
+ indexPath = fileURLToPath(new URL('../data/standards-guard-index.json', import.meta.url));
419
+ metaPath = fileURLToPath(new URL('../data/standards-guard-index.meta.json', import.meta.url));
420
+ }
421
+ catch {
422
+ // @silent-fallback-ok: malformed module URL means this candidate is unavailable;
423
+ // the caller returns configured-tree-unverified with the failed freshness claim
424
+ // visible in the report.
425
+ return null;
426
+ }
427
+ let bytes;
428
+ let parsed;
429
+ let meta;
430
+ try {
431
+ bytes = fs.readFileSync(indexPath);
432
+ parsed = JSON.parse(bytes.toString('utf-8'));
433
+ meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
434
+ }
435
+ catch {
436
+ // @silent-fallback-ok: a missing/unreadable index is not substituted or trusted.
437
+ // The caller exposes the absence as an unverified guard-tree basis.
438
+ return null;
439
+ }
440
+ const index = parsed;
441
+ const stamp = meta;
442
+ const observedSha256 = crypto.createHash('sha256').update(bytes).digest('hex');
443
+ const structurallyValid = index.schemaVersion === 1
444
+ && index.generatedFrom === 'source-tree'
445
+ && typeof index.registrySha256 === 'string'
446
+ && typeof index.packageVersion === 'string'
447
+ && Array.isArray(index.guards)
448
+ && index.guards.every((guard) => guard !== null
449
+ && typeof guard === 'object'
450
+ && typeof guard.ref === 'string'
451
+ && ['file', 'route', 'marker'].includes(guard.kind)
452
+ && typeof guard.refResolves === 'boolean');
453
+ const stampMatches = stamp.sha256 === observedSha256
454
+ && stamp.registrySha256 === registrySha256
455
+ && stamp.packageVersion === runningPackageVersion
456
+ && index.registrySha256 === registrySha256
457
+ && index.packageVersion === runningPackageVersion;
458
+ if (!structurallyValid || !stampMatches)
459
+ return null;
460
+ const indexedGuards = new Map();
461
+ for (const guard of index.guards) {
462
+ indexedGuards.set(guardKey(guard), { ...guard });
463
+ }
464
+ return {
465
+ provenance: {
466
+ projectDir: '(packed same-build source index)',
467
+ configuredProjectDir,
468
+ analyzable: true,
469
+ markersFound: ['packed guard evidence'],
470
+ basis: 'packed-source-index-match',
471
+ freshnessVerified: true,
472
+ freshnessReason: `the source evidence index sha256 ${observedSha256} matches its build meta, registry ` +
473
+ `sha256 ${registrySha256}, and running package version ${runningPackageVersion}`,
474
+ sourceIndexSha256: observedSha256,
475
+ registrySha256,
476
+ packageVersion: runningPackageVersion,
477
+ },
478
+ indexedGuards,
479
+ packedIndex: index,
480
+ };
481
+ }
482
+ function resolveGuardEvidence(opts, registryMarkdown) {
483
+ const registrySha256 = crypto.createHash('sha256').update(registryMarkdown).digest('hex');
484
+ const configuredProbe = probeGuardTree(opts.projectDir);
485
+ if (opts.integrity?.basis === 'packed-meta-match') {
486
+ const packed = readPackedGuardIndex(registrySha256, opts.integrity.runningPackageVersion, opts.projectDir);
487
+ if (packed && packed.packedIndex && opts.integrity.runningPackageVersion) {
488
+ const matched = findMatchingGuardTree(guardTreeCandidates(opts.projectDir), packed.packedIndex, registryMarkdown, opts.integrity.runningPackageVersion);
489
+ if (matched) {
490
+ const probe = probeGuardTree(matched);
491
+ const packageRoot = executingPackageRoot();
492
+ const executing = packageRoot !== null && sameDirectory(packageRoot, matched);
493
+ return {
494
+ provenance: {
495
+ ...probe,
496
+ configuredProjectDir: opts.projectDir,
497
+ basis: executing ? 'executing-source-tree' : 'source-tree-index-match',
498
+ freshnessVerified: true,
499
+ freshnessReason: `the live tree's complete audit-evidence index exactly matches packed source index ` +
500
+ `${packed.provenance.sourceIndexSha256}; landmark-only candidates were rejected`,
501
+ sourceIndexSha256: packed.provenance.sourceIndexSha256,
502
+ registrySha256,
503
+ packageVersion: opts.integrity.runningPackageVersion,
504
+ },
505
+ indexedGuards: null,
506
+ };
507
+ }
508
+ }
509
+ if (packed)
510
+ return packed;
511
+ }
512
+ return {
513
+ provenance: {
514
+ ...configuredProbe,
515
+ configuredProjectDir: opts.projectDir,
516
+ basis: 'configured-tree-unverified',
517
+ freshnessVerified: false,
518
+ freshnessReason: configuredProbe.analyzable
519
+ ? 'the configured tree has the audit landmarks, but no same-build identity ties it to the packed constitution; a coherent stale tree passes landmark probes by construction'
520
+ : 'the configured tree lacks the complete source landmarks and no valid same-build source index was available',
521
+ sourceIndexSha256: null,
522
+ registrySha256,
523
+ packageVersion: opts.integrity?.basis === 'packed-meta-match'
524
+ ? opts.integrity.runningPackageVersion
525
+ : null,
526
+ },
527
+ indexedGuards: null,
528
+ };
529
+ }
530
+ function verifyRefsFromResolution(refs, resolution, projectDir, routeTable, symbolIndex) {
531
+ if (resolution.indexedGuards === null) {
532
+ return verifyRefs(refs, projectDir, routeTable, symbolIndex);
533
+ }
534
+ return refs.map((ref) => {
535
+ const indexed = resolution.indexedGuards.get(`${ref.kind}\u0000${ref.ref}`);
536
+ return indexed ? { ...indexed } : { ref: ref.ref, kind: ref.kind, refResolves: false };
537
+ });
538
+ }
293
539
  /** Classify a standard by its strongest VERIFIED guard. */
294
540
  function classifyStandard(guards) {
295
541
  let best = null;
@@ -371,7 +617,7 @@ function guardStateSignal(projectDir, prior) {
371
617
  }
372
618
  return h.digest('hex').slice(0, 16);
373
619
  }
374
- export function computeInputHash(opts) {
620
+ function computeInputHashForResolution(opts, guardResolution) {
375
621
  // An UNREADABLE registry must not hash identically to an empty-but-present one:
376
622
  // that conflation is the honest-denominators shape at the cache layer, where a
377
623
  // missing constitution would share a cache slot with a genuinely blank one.
@@ -442,7 +688,15 @@ export function computeInputHash(opts) {
442
688
  const authoredSignal = authored === null
443
689
  ? 'no-authored-source'
444
690
  : crypto.createHash('sha256').update(authored).digest('hex').slice(0, 16);
445
- return `${regHash}.${auxHash}.${authoredSignal}.${repoStructureSignal(opts.projectDir)}`;
691
+ const guardBasisSignal = guardResolution
692
+ ? `${guardResolution.provenance.basis}:${guardResolution.provenance.sourceIndexSha256 ?? 'live'}:` +
693
+ `${guardResolution.provenance.projectDir}`
694
+ : 'guard-basis-not-resolved';
695
+ return `${regHash}.${auxHash}.${authoredSignal}.${repoStructureSignal(opts.projectDir)}.` +
696
+ crypto.createHash('sha256').update(guardBasisSignal).digest('hex').slice(0, 16);
697
+ }
698
+ export function computeInputHash(opts) {
699
+ return computeInputHashForResolution(opts);
446
700
  }
447
701
  /**
448
702
  * Derive how much the assessment has EARNED. Extracted as a function on purpose:
@@ -518,34 +772,28 @@ authoredSource) {
518
772
  };
519
773
  }
520
774
  const verdict = earnsVerified(integrity);
521
- if (verdict.verified) {
775
+ if (!verdict.verified) {
776
+ return {
777
+ confidence: 'unverified',
778
+ reason: `internal checks passed over ${total} standards parsed from ${registry.path} ` +
779
+ `(${registry.bytes} bytes), but ${verdict.reason} — so nothing establishes this is the CURRENT ` +
780
+ `constitution rather than a coherent older copy; a stale registry passes every internal check ` +
781
+ `by construction`,
782
+ };
783
+ }
784
+ if (guards && !guards.freshnessVerified) {
522
785
  return {
523
- confidence: 'verified',
524
- reason: `${total} standards parsed from ${registry.path}, and ${verdict.reason}. ` +
525
- // The honest scope limit, stated IN the verdict rather than left to be inferred.
526
- //
527
- // The REGISTRY half now carries three currency operands. The GUARD half carries
528
- // none: refs resolve against whatever src/ sits in projectDir, and on a deployed
529
- // agent that is a copy which can be months older than the constitution being
530
- // graded (measured: an agent home's routes.ts dated May 28 against a current
531
- // registry). So `verified` speaks for the constitution's currency, NOT for the
532
- // currency of the tree its guards were looked up in.
533
- //
534
- // Naming the limit rather than silently widening the word is the same repair this
535
- // change already made twice. Establishing guard-tree currency is a separate job
536
- // and is recorded as one, not quietly folded in here.
537
- `SCOPE NOTE: this verdict covers the CONSTITUTION's currency only. The enforcement ` +
538
- `refs were resolved against ${guards?.projectDir ?? 'the configured project dir'}, ` +
539
- `whose currency is not established by anything here — on a deployed agent that tree ` +
540
- `can be older than the constitution being graded`,
786
+ confidence: 'untrustworthy',
787
+ reason: `the constitution is package-stamped, but guard-tree freshness was not established: ` +
788
+ `${guards.freshnessReason}. The coverage counts remain observable for diagnosis, but a stale ` +
789
+ `tree passes capability landmarks by construction and cannot support a trustworthy ratio`,
541
790
  };
542
791
  }
543
792
  return {
544
- confidence: 'unverified',
545
- reason: `internal checks passed over ${total} standards parsed from ${registry.path} ` +
546
- `(${registry.bytes} bytes), but ${verdict.reason} so nothing establishes this is the CURRENT ` +
547
- `constitution rather than a coherent older copy; a stale registry passes every internal check ` +
548
- `by construction`,
793
+ confidence: 'verified',
794
+ reason: `${total} standards parsed from ${registry.path}, and ${verdict.reason}. ` +
795
+ `The guard evidence is also current: ${guards?.freshnessReason ?? 'the caller supplied no guard provenance'}. ` +
796
+ `This is package/build currency, not a runtime execution check and not tamper-resistant`,
549
797
  };
550
798
  }
551
799
  /**
@@ -590,7 +838,18 @@ export function probeGuardTree(projectDir) {
590
838
  // `analyzable` permanently false the moment anyone adds a third `check()` — which is
591
839
  // exactly the failure this probe was just repaired from, encoded so it can recur.
592
840
  const analyzable = markersFound.includes('src/server/routes.ts') && markersFound.includes('src/core');
593
- return { projectDir, analyzable, markersFound };
841
+ return {
842
+ projectDir,
843
+ configuredProjectDir: projectDir,
844
+ analyzable,
845
+ markersFound,
846
+ basis: 'configured-tree-unverified',
847
+ freshnessVerified: false,
848
+ freshnessReason: 'landmark presence proves analyzability, not source-tree identity or freshness',
849
+ sourceIndexSha256: null,
850
+ registrySha256: null,
851
+ packageVersion: null,
852
+ };
594
853
  }
595
854
  /**
596
855
  * Refuse a basis that does not belong to these bytes.
@@ -648,7 +907,15 @@ export function compareAgainstAuthoredSource(projectDir, gradedMarkdown) {
648
907
  return { answerable: true, matches: authored === gradedMarkdown };
649
908
  }
650
909
  export function computeCoverage(opts, prior) {
651
- const inputHash = computeInputHash(opts);
910
+ // Read once before source resolution: the guard index is tied to these exact
911
+ // registry bytes, and candidate checkouts are selected by reproducing that index.
912
+ const registryMarkdown = opts.registryMarkdown ?? fs.readFileSync(opts.registryPath, 'utf-8');
913
+ assertResolutionCoherent(opts, registryMarkdown);
914
+ const guardResolution = resolveGuardEvidence(opts, registryMarkdown);
915
+ const inputHash = computeInputHashForResolution(opts, guardResolution);
916
+ const currentGuardSignal = guardResolution.provenance.sourceIndexSha256 !== null
917
+ ? `source-index:${guardResolution.provenance.sourceIndexSha256}`
918
+ : guardStateSignal(guardResolution.provenance.projectDir, prior);
652
919
  // TWO conditions, not one.
653
920
  //
654
921
  // `inputHash` covers the registry bytes, the basis, the paths and a coarse repo
@@ -664,14 +931,13 @@ export function computeCoverage(opts, prior) {
664
931
  // prior-derived value into the key makes the first computation (no prior) and the
665
932
  // second (with prior) disagree by construction, so the short-circuit would never fire
666
933
  // — which is how the first attempt at this failed, caught by an existing test.
667
- if (prior && prior.inputHash === inputHash && prior.guardSignal === guardStateSignal(opts.projectDir, prior)) {
934
+ if (prior && prior.inputHash === inputHash && prior.guardSignal === currentGuardSignal) {
668
935
  // Inputs unchanged → the deterministic report is byte-identical to the prior;
669
936
  // return it (only its timestamp would differ on recompute). The short-circuit.
670
937
  return prior;
671
938
  }
672
939
  // Read ONCE and keep what the parse saw: the audit must be able to state the
673
940
  // denominator it computed over (honest-denominators instance 4).
674
- const registryMarkdown = opts.registryMarkdown ?? fs.readFileSync(opts.registryPath, 'utf-8');
675
941
  const { articles, diagnostics } = parseStandardsRegistryDetailed(registryMarkdown);
676
942
  const canary = runRegistryCanary(articles, diagnostics);
677
943
  const registry = {
@@ -685,20 +951,24 @@ export function computeCoverage(opts, prior) {
685
951
  canaryOk: canary.ok,
686
952
  canaryFailures: canary.failures,
687
953
  };
688
- assertResolutionCoherent(opts, registryMarkdown);
689
- const guards = probeGuardTree(opts.projectDir);
690
- const routeTable = loadRouteTable(opts.projectDir);
954
+ const guards = guardResolution.provenance;
955
+ const liveProjectDir = guards.projectDir;
956
+ const routeTable = guardResolution.indexedGuards === null
957
+ ? loadRouteTable(liveProjectDir)
958
+ : new Set();
691
959
  // Collect every wanted marker across all articles → ONE bounded src walk.
692
960
  const extracted = articles.map((a) => ({ a, refs: extractEnforcementRefs(a) }));
693
961
  const wantedMarkers = new Set();
694
962
  for (const { refs } of extracted)
695
963
  for (const m of refs.markers)
696
964
  wantedMarkers.add(m);
697
- const symbolIndex = buildSymbolIndex(opts.projectDir, wantedMarkers);
965
+ const symbolIndex = guardResolution.indexedGuards === null
966
+ ? buildSymbolIndex(liveProjectDir, wantedMarkers)
967
+ : new Set();
698
968
  const classifiedAt = new Date().toISOString();
699
969
  const standards = extracted.map(({ a, refs }) => {
700
970
  const flat = flattenRefs(refs);
701
- const guards = verifyRefs(flat, opts.projectDir, routeTable, symbolIndex);
971
+ const guards = verifyRefsFromResolution(flat, guardResolution, liveProjectDir, routeTable, symbolIndex);
702
972
  const enforcementKind = classifyStandard(guards);
703
973
  const danglingRefs = guards.filter((g) => !g.refResolves).map((g) => g.ref).sort();
704
974
  return { standard: a.name, family: a.family, enforcementKind, guards, danglingRefs, classifiedAt };
@@ -716,7 +986,9 @@ export function computeCoverage(opts, prior) {
716
986
  const enforcedRatio = total === 0 ? null : Number((enforced / total).toFixed(4));
717
987
  const gaps = standards.filter((s) => s.enforcementKind === 'documented-only').map((s) => s.standard);
718
988
  const danglingCount = standards.reduce((n, s) => n + s.danglingRefs.length, 0);
719
- const { confidence: assessmentConfidence, reason: confidenceReason } = deriveAssessmentConfidence(total, registry, opts.integrity, guards, compareAgainstAuthoredSource(opts.projectDir, registryMarkdown));
989
+ const { confidence: assessmentConfidence, reason: confidenceReason } = deriveAssessmentConfidence(total, registry, opts.integrity, guards, guards.basis === 'executing-source-tree' || guards.basis === 'source-tree-index-match'
990
+ ? compareAgainstAuthoredSource(liveProjectDir, registryMarkdown)
991
+ : { answerable: false, matches: false });
720
992
  const report = {
721
993
  generatedAt: classifiedAt,
722
994
  inputHash,
@@ -741,7 +1013,9 @@ export function computeCoverage(opts, prior) {
741
1013
  },
742
1014
  };
743
1015
  // Computed AFTER the standards exist, because it digests the refs this pass resolved.
744
- report.guardSignal = guardStateSignal(opts.projectDir, report);
1016
+ report.guardSignal = guardResolution.provenance.sourceIndexSha256 !== null
1017
+ ? `source-index:${guardResolution.provenance.sourceIndexSha256}`
1018
+ : guardStateSignal(liveProjectDir, report);
745
1019
  return report;
746
1020
  }
747
1021
  /**
@@ -792,7 +1066,18 @@ export function unusableCoverageReport(resolution) {
792
1066
  registry,
793
1067
  // No pass ran, so the guard tree was never probed. Reporting `analyzable:
794
1068
  // false` here would assert a fact about the repo that this path never checked.
795
- guards: { projectDir: '(not probed — the registry was unusable)', analyzable: false, markersFound: [] },
1069
+ guards: {
1070
+ projectDir: '(not probed — the registry was unusable)',
1071
+ configuredProjectDir: '(unknown)',
1072
+ analyzable: false,
1073
+ markersFound: [],
1074
+ basis: 'not-probed',
1075
+ freshnessVerified: false,
1076
+ freshnessReason: 'the registry was unusable, so no guard source was selected',
1077
+ sourceIndexSha256: null,
1078
+ registrySha256: registry.sha256,
1079
+ packageVersion: null,
1080
+ },
796
1081
  },
797
1082
  };
798
1083
  }