harness-everything 0.36.1 → 0.36.2

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.
@@ -10,7 +10,7 @@
10
10
  "name": "harness-everything",
11
11
  "source": "./",
12
12
  "description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
13
- "version": "0.36.1",
13
+ "version": "0.36.2",
14
14
  "author": {
15
15
  "name": "Miya Daniel"
16
16
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "harness-everything",
3
3
  "description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
4
- "version": "0.36.1",
4
+ "version": "0.36.2",
5
5
  "author": {
6
6
  "name": "Miya Daniel"
7
7
  },
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.36.2](https://github.com/dyphn1/Harness-everything/compare/v0.36.1...v0.36.2) (2026-10-20)
6
+
7
+ ### Bug Fixes
8
+
9
+ * **doc-lifecycle:** honor poisoned shared destinations inside metadata ([2c69247](https://github.com/dyphn1/Harness-everything/commit/2c69247c3a2e108df945fc91c68fb060ac04e058)), closes [#340](https://github.com/dyphn1/Harness-everything/issues/340)
10
+ * **doc-lifecycle:** keep rejected destination snapshots single-read ([f49b253](https://github.com/dyphn1/Harness-everything/commit/f49b25318f4022d241d91051e1daa51ef61cdff2)), closes [#340](https://github.com/dyphn1/Harness-everything/issues/340)
11
+ * **doc-lifecycle:** single-read the whole-baseline and destination digests ([063a00b](https://github.com/dyphn1/Harness-everything/commit/063a00b4f97c147299a63d7b9b82fc2cbae87bc0)), closes [#340](https://github.com/dyphn1/Harness-everything/issues/340)
12
+
5
13
  ## [0.36.1](https://github.com/dyphn1/Harness-everything/compare/v0.36.0...v0.36.1) (2026-10-20)
6
14
 
7
15
  ### Bug Fixes
@@ -66,10 +74,12 @@ All notable changes to this project are documented in this file.
66
74
  * **doc-evidence:** defer the candidate-inventory prototype check to applicable baseline-only obligations and treat a throwing prototype trap as unknown inventory, so unrelated obligations are never aborted
67
75
  * **doc-evidence:** bind baseline-annotated mixed scopes and rename destinations to the captured candidate snapshot for evidence binding, the audit identity, and stale diagnostics, so a later-throwing or later-changing digest accessor cannot disagree with the digest that bound evidence
68
76
  * **doc-evidence:** carry the metadata-validated rename destination digest and baseline-path source-absence proof through capture and binding, so a candidate inventory that answers consistently during validation is never re-inspected and cannot abort the check or drift the audit key
77
+ * **doc-evidence:** keep rejected rename destination snapshots single-read, so a throwing or unusable destination digest getter is never re-read and its later values never reach the capture or audit identity, including shared destinations where a poisoned destination is honored inside metadata validation and wins in the audit
69
78
  * **doc-evidence:** restrict the validated source-absence audit shortcut to applicable obligations and require every accepted entry sharing a rename destination to agree on the validated own digest, so inapplicable audit identity keeps the legacy read and contradictory shared-destination snapshots fail closed
70
79
  * **doc-evidence:** gate the rename destination snapshot on applicability and guard baselinePaths entry inspection, so inapplicable audit identity reports the first legacy digest value and a throwing entry getter or descriptor trap fails the entry closed instead of escaping the checker
71
80
  * **doc-evidence:** capture the validated per-path baseline digest during metadata and drive the audit mirror, stale-baseline diagnostics, and binding from that snapshot, so a stateful baseline getter cannot bind a record against a value it started returning only after validation and a throwing inspection is a metadata error instead of an escape
72
81
  * **doc-evidence:** key the baseline audit mirror by the metadata-validated path snapshot instead of re-reading `baselinePaths[].path`, so a stateful path accessor read during validation can never make a passing result describe a different baseline identity
82
+ * **doc-evidence:** read the whole-baseline `baseline.digest` and the rename destination's own digest exactly once and validate that captured scalar, so a stateful getter cannot pass validation as one value while evidence binds its later value; identity and destination inspection failures fail closed as structured metadata errors instead of escaping the checker
73
83
 
74
84
  ## [0.34.0](https://github.com/dyphn1/Harness-everything/compare/v0.33.0...v0.34.0) (2026-10-17)
75
85
 
@@ -1877,7 +1877,9 @@ checkCase('335 P3 the metadata-validated rename destination digest is carried wi
1877
1877
  for (const target of [modules, packageModules]) {
1878
1878
  const label = target === modules ? 'canonical' : 'packaged';
1879
1879
  const ctx = prepare('rename', target);
1880
- const counted = countingDigestEntry(RENAME_CAPTURED, { validReads: 3, later: 'throw' });
1880
+ // P3(r4321077631): the destination snapshot reads its own digest exactly once, so a getter
1881
+ // that throws after that single valid read is never reached and the captured B still binds.
1882
+ const counted = countingDigestEntry(RENAME_CAPTURED, { validReads: 1, later: 'throw' });
1881
1883
  const artifacts = { ...ctx.artifacts, [RENAME_TO]: counted.entry };
1882
1884
  const record = execRecord(ctx, 'pair-run', 'doc-change:doc-lint', {
1883
1885
  paths: [RENAME_FROM, RENAME_TO], candidatePaths: [], baselinePaths: [RENAME_FROM],
@@ -1892,12 +1894,74 @@ checkCase('335 P3 the metadata-validated rename destination digest is carried wi
1892
1894
  if (!result.artifactDigest.includes(`${RENAME_TO}=${RENAME_CAPTURED}`)) {
1893
1895
  failures.push(`${label}: audit=${result.artifactDigest}`);
1894
1896
  }
1895
- if (counted.reads() !== 3) failures.push(`${label}: destination reads=${counted.reads()}`);
1897
+ if (counted.reads() !== 1) failures.push(`${label}: destination reads=${counted.reads()}`);
1896
1898
  }
1897
1899
  assert.deepStrictEqual(failures, [],
1898
1900
  `the validated destination digest must be carried: ${failures.join('; ')}`);
1899
1901
  });
1900
1902
 
1903
+ // #342 review P3: a destination snapshot rejected during metadata (throwing or unusable own
1904
+ // digest) is still a visited snapshot. The capture and audit lanes must never re-read it: the
1905
+ // single metadata read stays the only digest read, and a stateful getter cannot ship a later
1906
+ // value into the audit identity even though its entry failed.
1907
+ checkCase('#342 P3 a rejected destination snapshot is read exactly once even when its digest getter throws', () => {
1908
+ const failures = [];
1909
+ for (const target of [modules, packageModules]) {
1910
+ const label = target === modules ? 'canonical' : 'packaged';
1911
+ const ctx = prepare('rename', target);
1912
+ const counted = countingDigestEntry(RENAME_CAPTURED, { validReads: 0, later: 'throw' });
1913
+ const artifacts = { ...ctx.artifacts, [RENAME_TO]: counted.entry };
1914
+ const record = execRecord(ctx, 'move-run', 'doc-change:doc-lint', {
1915
+ paths: [RENAME_FROM, RENAME_TO], candidatePaths: [RENAME_TO], baselinePaths: [RENAME_FROM]
1916
+ });
1917
+ let out;
1918
+ try {
1919
+ out = runDocLint(ctx, target, { artifacts, records: [record] });
1920
+ } catch (error) { failures.push(`${label}: escaped: ${error.message}`); continue; }
1921
+ const result = resultOf(out, 'doc-change:doc-lint');
1922
+ if (result.outcome !== 'none') failures.push(`${label}: outcome=${result.outcome}`);
1923
+ if (!diagnosticCodes(out).includes('candidate-artifacts-invalid')) {
1924
+ failures.push(`${label}: diagnostics=${diagnosticCodes(out)}`);
1925
+ }
1926
+ if (counted.reads() !== 1) failures.push(`${label}: destination reads=${counted.reads()}`);
1927
+ if (!result.artifactDigest.includes(`${RENAME_TO}=missing`)) {
1928
+ failures.push(`${label}: audit=${result.artifactDigest}`);
1929
+ }
1930
+ }
1931
+ assert.deepStrictEqual(failures, [],
1932
+ `a rejected throwing destination must be read once and never again: ${failures.join('; ')}`);
1933
+ });
1934
+
1935
+ checkCase('#342 P3 an unusable first destination read never ships a later non-empty value into the audit', () => {
1936
+ const failures = [];
1937
+ for (const target of [modules, packageModules]) {
1938
+ const label = target === modules ? 'canonical' : 'packaged';
1939
+ const ctx = prepare('rename', target);
1940
+ // First read: unusable ''. Any later read (which must not happen) returns non-empty
1941
+ // RENAME_LATER, so the audit would describe it if the rejected snapshot were re-read.
1942
+ const counted = countingDigestEntry('', { validReads: 1, later: 'value', laterValue: RENAME_LATER });
1943
+ const artifacts = { ...ctx.artifacts, [RENAME_TO]: counted.entry };
1944
+ const record = execRecord(ctx, 'move-run', 'doc-change:doc-lint', {
1945
+ paths: [RENAME_FROM, RENAME_TO], candidatePaths: [RENAME_TO], baselinePaths: [RENAME_FROM]
1946
+ });
1947
+ let out;
1948
+ try {
1949
+ out = runDocLint(ctx, target, { artifacts, records: [record] });
1950
+ } catch (error) { failures.push(`${label}: escaped: ${error.message}`); continue; }
1951
+ const result = resultOf(out, 'doc-change:doc-lint');
1952
+ if (result.outcome !== 'none') failures.push(`${label}: outcome=${result.outcome}`);
1953
+ if (counted.reads() !== 1) failures.push(`${label}: destination reads=${counted.reads()}`);
1954
+ if (result.artifactDigest.includes(RENAME_LATER)) {
1955
+ failures.push(`${label}: audit describes a later value: ${result.artifactDigest}`);
1956
+ }
1957
+ if (!result.artifactDigest.includes(`${RENAME_TO}=missing`)) {
1958
+ failures.push(`${label}: audit=${result.artifactDigest}`);
1959
+ }
1960
+ }
1961
+ assert.deepStrictEqual(failures, [],
1962
+ `an unusable destination read must stay single-read and 'missing': ${failures.join('; ')}`);
1963
+ });
1964
+
1901
1965
  checkCase('335 P3 a stale pair plus a destination-only record stays none under descriptor patience', () => {
1902
1966
  const failures = [];
1903
1967
  for (const target of [modules, packageModules]) {
@@ -2116,10 +2180,12 @@ checkCase('335 P3 two accepted entries sharing a rename destination must agree o
2116
2180
  }
2117
2181
  }
2118
2182
 
2119
- // Dynamic shared destination: D1 for the first entry's metadata, D2 for the second.
2183
+ // Dynamic shared destination: D1 for the first entry's single-read snapshot, D2 for the
2184
+ // second (the per-entry read advances the getter). P3(r4321077631) made each entry's read
2185
+ // exactly one, so the fixture flips after one valid read.
2120
2186
  {
2121
2187
  const { ctx } = sharedDestinationContext(target);
2122
- const dynamic = countingDigestEntry(RENAME_CAPTURED, { validReads: 3, later: 'value', laterValue: RENAME_LATER });
2188
+ const dynamic = countingDigestEntry(RENAME_CAPTURED, { validReads: 1, later: 'value', laterValue: RENAME_LATER });
2123
2189
  const artifacts = { ...ctx.artifacts, [RENAME_TO]: dynamic.entry };
2124
2190
  const out = run(artifacts, [
2125
2191
  sharedPairRecord(ctx, 'pair-run', RENAME_CAPTURED),
@@ -2177,6 +2243,69 @@ checkCase('335 P3 two accepted entries sharing a rename destination must agree o
2177
2243
  `shared rename destinations must agree on the validated digest: ${failures.join('; ')}`);
2178
2244
  });
2179
2245
 
2246
+ // #342 review P3: a shared rename destination whose snapshot is rejected must stay honored.
2247
+ // invalid→valid: the first declaring entry fails its own snapshot; a later declaring entry must
2248
+ // fail closed without reading the destination again (one read total, audit 'missing').
2249
+ // valid→throw: the first entry carries a validated success; the second declaration's read
2250
+ // throws, and the poisoned visited state wins in the audit over the previously seeded success
2251
+ // (one read per declaring entry, two total).
2252
+ checkCase('#342 P3 a rejected shared destination is never read again and keeps audit precedence', () => {
2253
+ const failures = [];
2254
+ for (const target of [modules, packageModules]) {
2255
+ const label = target === modules ? 'canonical' : 'packaged';
2256
+
2257
+ // invalid→valid: first own read unusable, later reads (which must not happen) valid.
2258
+ {
2259
+ const { ctx, obligations, baseline } = sharedDestinationContext(target);
2260
+ const counted = countingDigestEntry('', { validReads: 1, later: 'value', laterValue: RENAME_CAPTURED });
2261
+ // Read counts are scoped to the obligation under test; unrelated obligations keep the
2262
+ // designed legacy candidate read, exactly as runDocLint's single-obligation runs do.
2263
+ const out = target.checkDocObligations({
2264
+ obligations: obligations.filter(item => item.id === 'doc-change:doc-lint'),
2265
+ artifacts: { ...ctx.artifacts, [RENAME_TO]: counted.entry }, baseline,
2266
+ executionRecords: [sharedPairRecord(ctx, 'pair-run', RENAME_CAPTURED)], reviewRecords: []
2267
+ });
2268
+ const result = resultOf(out, 'doc-change:doc-lint');
2269
+ if (result.outcome !== 'none') failures.push(`${label}/invalid-to-valid: outcome=${result.outcome}`);
2270
+ if (!diagnosticCodes(out).includes('baseline-path-invalid')) {
2271
+ failures.push(`${label}/invalid-to-valid: diagnostics=${diagnosticCodes(out)}`);
2272
+ }
2273
+ if (counted.reads() !== 1) failures.push(`${label}/invalid-to-valid: destination reads=${counted.reads()}`);
2274
+ if (result.artifactDigest.includes(RENAME_CAPTURED)) {
2275
+ failures.push(`${label}/invalid-to-valid: audit describes a later value: ${result.artifactDigest}`);
2276
+ }
2277
+ if (!result.artifactDigest.includes(`${RENAME_TO}=missing`)) {
2278
+ failures.push(`${label}/invalid-to-valid: audit=${result.artifactDigest}`);
2279
+ }
2280
+ }
2281
+
2282
+ // valid→throw: successful first snapshot, then a throwing own read on the second entry.
2283
+ {
2284
+ const { ctx, obligations, baseline } = sharedDestinationContext(target);
2285
+ const counted = countingDigestEntry(RENAME_CAPTURED, { validReads: 1, later: 'throw' });
2286
+ const out = target.checkDocObligations({
2287
+ obligations: obligations.filter(item => item.id === 'doc-change:doc-lint'),
2288
+ artifacts: { ...ctx.artifacts, [RENAME_TO]: counted.entry }, baseline,
2289
+ executionRecords: [sharedPairRecord(ctx, 'pair-run', RENAME_CAPTURED)], reviewRecords: []
2290
+ });
2291
+ const result = resultOf(out, 'doc-change:doc-lint');
2292
+ if (result.outcome !== 'none') failures.push(`${label}/valid-to-throw: outcome=${result.outcome}`);
2293
+ if (!diagnosticCodes(out).includes('candidate-artifacts-invalid')) {
2294
+ failures.push(`${label}/valid-to-throw: diagnostics=${diagnosticCodes(out)}`);
2295
+ }
2296
+ if (counted.reads() !== 2) failures.push(`${label}/valid-to-throw: destination reads=${counted.reads()}`);
2297
+ if (result.artifactDigest.includes(RENAME_CAPTURED)) {
2298
+ failures.push(`${label}/valid-to-throw: audit describes a rejected-state value: ${result.artifactDigest}`);
2299
+ }
2300
+ if (!result.artifactDigest.includes(`${RENAME_TO}=missing`)) {
2301
+ failures.push(`${label}/valid-to-throw: audit=${result.artifactDigest}`);
2302
+ }
2303
+ }
2304
+ }
2305
+ assert.deepStrictEqual(failures, [],
2306
+ `a rejected shared destination must stay single-read and audited as 'missing': ${failures.join('; ')}`);
2307
+ });
2308
+
2180
2309
  // ---------------------------------------------------------------------------
2181
2310
  // P2 applicability-gated destination snapshot (review r4318520818 at 56f3094):
2182
2311
  // N/A and unknown obligations never bind evidence, so their destination digest
@@ -2220,10 +2349,11 @@ checkCase('335 P2 an inapplicable rename never consumes the destination snapshot
2220
2349
  }
2221
2350
 
2222
2351
  // Positive control: the applicable obligation still captures the destination snapshot
2223
- // once during metadata and binds against it.
2352
+ // once during metadata and binds against it. P3(r4321077631) made that capture a
2353
+ // single guarded read, so the getter is never consumed beyond its first valid read.
2224
2354
  {
2225
2355
  const ctx = prepare('rename', target);
2226
- const counted = countingDigestEntry(RENAME_CAPTURED, { validReads: 3, later: 'value', laterValue: RENAME_LATER });
2356
+ const counted = countingDigestEntry(RENAME_CAPTURED, { validReads: 1, later: 'throw' });
2227
2357
  const artifacts = { ...ctx.artifacts, [RENAME_TO]: counted.entry };
2228
2358
  const record = {
2229
2359
  ...execRecord(ctx, 'pair-run', 'doc-change:doc-lint', {
@@ -2235,7 +2365,7 @@ checkCase('335 P2 an inapplicable rename never consumes the destination snapshot
2235
2365
  const out = checkArtifacts(ctx, artifacts, { obligations: [obligation], executionRecords: [record] }, target);
2236
2366
  const result = resultOf(out, 'doc-change:doc-lint');
2237
2367
  if (result.outcome !== 'pass') failures.push(`${label}/applicable: outcome=${result.outcome}`);
2238
- if (counted.reads() !== 3) failures.push(`${label}/applicable: destination getter reads=${counted.reads()}`);
2368
+ if (counted.reads() !== 1) failures.push(`${label}/applicable: destination getter reads=${counted.reads()}`);
2239
2369
  }
2240
2370
  }
2241
2371
  assert.deepStrictEqual(failures, [],
@@ -2687,4 +2817,222 @@ checkCase('335 P2 the audit mirror reports the metadata-validated path snapshot,
2687
2817
  `the audit mirror must use the validated path snapshot: ${failures.join('; ')}`);
2688
2818
  });
2689
2819
 
2820
+ //----------------------------------------------------------------------------
2821
+ // P2(r4321077631/r4321077638) at 7217b3a: whole-baseline identity and rename
2822
+ // destination digests must each be read exactly once — the same scalar is
2823
+ // validated and captured, so a stateful getter cannot pass validation as one
2824
+ // value while evidence binds its later value.
2825
+ //----------------------------------------------------------------------------
2826
+
2827
+ const RENAME_OLD = 'docs/adr/old.md';
2828
+ const RENAME_NEW = 'docs/adr/new.md';
2829
+ const RENAME_BASELINE_DIGEST = sha256('rename whole baseline');
2830
+ const RENAME_OLD_BASELINE = sha256('old document baseline content');
2831
+ const RENAME_DEST_B = sha256('destination content B');
2832
+ const RENAME_DEST_C = sha256('destination content C');
2833
+
2834
+ checkCase('335 P2 the whole-baseline digest is validated and captured from one read', () => {
2835
+ const failures = [];
2836
+ for (const target of [modules, packageModules]) {
2837
+ const label = target === modules ? 'canonical' : 'packaged';
2838
+ const PATH = BASELINE_EXAMPLE;
2839
+
2840
+ function runWith(baseline, recordBaseDigest) {
2841
+ return target.checkDocObligations({
2842
+ obligations: [{
2843
+ id: 'doc-change:doc-lint',
2844
+ capability: 'doc-lint',
2845
+ level: 'MUST',
2846
+ applicability: 'applicable',
2847
+ scope: { paths: [PATH], baselinePaths: [{ path: PATH, reason: 'deleted' }] }
2848
+ }],
2849
+ artifacts: {},
2850
+ baseline,
2851
+ executionRecords: [{
2852
+ id: 'tomb-run',
2853
+ obligationId: 'doc-change:doc-lint',
2854
+ command: 'node ci/mechanism-335-baseline-evidence.test.js # tomb-run',
2855
+ exitCode: 0,
2856
+ provenance: 'baseline identity snapshot regression',
2857
+ baseDigest: recordBaseDigest,
2858
+ paths: [PATH],
2859
+ artifactDigests: {},
2860
+ baselineArtifacts: { [PATH]: BASELINE_EXAMPLE_VALUE }
2861
+ }],
2862
+ reviewRecords: []
2863
+ });
2864
+ }
2865
+
2866
+ // Stateful identity getter: validation must use the first read; a record attesting to the
2867
+ // later value cannot pass.
2868
+ {
2869
+ let identityReads = 0;
2870
+ const baseline = {
2871
+ get digest() {
2872
+ identityReads += 1;
2873
+ return identityReads === 1 ? 'baseline-A' : 'baseline-B';
2874
+ },
2875
+ artifacts: { [PATH]: BASELINE_EXAMPLE_VALUE }
2876
+ };
2877
+ let out;
2878
+ try {
2879
+ out = runWith(baseline, 'baseline-B');
2880
+ } catch (error) {
2881
+ failures.push(`${label}/stateful: escaped: ${error.message}`);
2882
+ continue;
2883
+ }
2884
+ const result = out.results.find(item => item.obligationId === 'doc-change:doc-lint');
2885
+ if (identityReads !== 1) failures.push(`${label}/stateful: identity getter reads=${identityReads}`);
2886
+ if (result.outcome === 'pass') failures.push(`${label}/stateful: a record bound to the later value passed`);
2887
+ if (!out.diagnostics.some(item => item.code === 'stale-base-digest')
2888
+ && !out.diagnostics.some(item => item.code === 'baseline-identity-missing')) {
2889
+ failures.push(`${label}/stateful: diagnostics=${out.diagnostics.map(item => item.code)}`);
2890
+ }
2891
+ }
2892
+
2893
+ // Stable controls: the validated identity binds; a different identity does not.
2894
+ {
2895
+ const passing = runWith(
2896
+ { digest: 'baseline-A', artifacts: { [PATH]: BASELINE_EXAMPLE_VALUE } }, 'baseline-A');
2897
+ if (passing.results.find(item => item.obligationId === 'doc-change:doc-lint').outcome !== 'pass') {
2898
+ failures.push(`${label}/stable: the validated identity did not pass`);
2899
+ }
2900
+ const stale = runWith(
2901
+ { digest: 'baseline-B', artifacts: { [PATH]: BASELINE_EXAMPLE_VALUE } }, 'baseline-A');
2902
+ const staleResult = stale.results.find(item => item.obligationId === 'doc-change:doc-lint');
2903
+ if (staleResult.outcome !== 'none') failures.push(`${label}/stable: outcome=${staleResult.outcome}`);
2904
+ if (!stale.diagnostics.some(item => item.code === 'stale-base-digest')) {
2905
+ failures.push(`${label}/stable: diagnostics=${stale.diagnostics.map(item => item.code)}`);
2906
+ }
2907
+ }
2908
+
2909
+ // A throwing identity getter fails closed as a metadata error instead of escaping.
2910
+ {
2911
+ const baseline = {
2912
+ get digest() {
2913
+ throw new Error('baseline identity getter');
2914
+ },
2915
+ artifacts: { [PATH]: BASELINE_EXAMPLE_VALUE }
2916
+ };
2917
+ let out;
2918
+ try {
2919
+ out = runWith(baseline, 'baseline-A');
2920
+ } catch (error) {
2921
+ failures.push(`${label}/throwing: escaped: ${error.message}`);
2922
+ continue;
2923
+ }
2924
+ const result = out.results.find(item => item.obligationId === 'doc-change:doc-lint');
2925
+ if (result.outcome === 'pass') failures.push(`${label}/throwing: a throwing identity produced a pass`);
2926
+ if (!out.diagnostics.some(item => item.code === 'baseline-identity-missing')) {
2927
+ failures.push(`${label}/throwing: diagnostics=${out.diagnostics.map(item => item.code)}`);
2928
+ }
2929
+ }
2930
+ }
2931
+ assert.deepStrictEqual(failures, [],
2932
+ `the whole-baseline identity must be captured once: ${failures.join('; ')}`);
2933
+ });
2934
+
2935
+ checkCase('335 P2 the rename destination digest is validated and captured from one read', () => {
2936
+ const failures = [];
2937
+ for (const target of [modules, packageModules]) {
2938
+ const label = target === modules ? 'canonical' : 'packaged';
2939
+ const obligation = {
2940
+ id: 'doc-change:doc-lint',
2941
+ capability: 'doc-lint',
2942
+ level: 'MUST',
2943
+ applicability: 'applicable',
2944
+ scope: {
2945
+ paths: [RENAME_OLD, RENAME_NEW],
2946
+ baselinePaths: [{ path: RENAME_OLD, reason: 'rename-from', renameTo: RENAME_NEW }]
2947
+ }
2948
+ };
2949
+ const baseline = {
2950
+ digest: RENAME_BASELINE_DIGEST,
2951
+ artifacts: { [RENAME_OLD]: RENAME_OLD_BASELINE }
2952
+ };
2953
+
2954
+ function runWith(destinationEntry, recordDestinationDigest) {
2955
+ return target.checkDocObligations({
2956
+ obligations: [obligation],
2957
+ artifacts: { [RENAME_NEW]: destinationEntry },
2958
+ baseline,
2959
+ executionRecords: [{
2960
+ id: 'rename-run',
2961
+ obligationId: 'doc-change:doc-lint',
2962
+ command: 'node ci/mechanism-335-baseline-evidence.test.js # rename-run',
2963
+ exitCode: 0,
2964
+ provenance: 'rename destination snapshot regression',
2965
+ baseDigest: RENAME_BASELINE_DIGEST,
2966
+ paths: [RENAME_OLD, RENAME_NEW],
2967
+ artifactDigests: { [RENAME_NEW]: recordDestinationDigest },
2968
+ baselineArtifacts: { [RENAME_OLD]: RENAME_OLD_BASELINE }
2969
+ }],
2970
+ reviewRecords: []
2971
+ });
2972
+ }
2973
+
2974
+ // Stateful destination digest getter: validation must use the first read B; a record
2975
+ // carrying only the later C cannot pass.
2976
+ {
2977
+ let destinationReads = 0;
2978
+ const statefulDestination = {
2979
+ get digest() {
2980
+ destinationReads += 1;
2981
+ return destinationReads === 1 ? RENAME_DEST_B : RENAME_DEST_C;
2982
+ }
2983
+ };
2984
+ let out;
2985
+ try {
2986
+ out = runWith(statefulDestination, RENAME_DEST_C);
2987
+ } catch (error) {
2988
+ failures.push(`${label}/stateful: escaped: ${error.message}`);
2989
+ continue;
2990
+ }
2991
+ const result = out.results.find(item => item.obligationId === 'doc-change:doc-lint');
2992
+ if (destinationReads !== 1) failures.push(`${label}/stateful: destination digest reads=${destinationReads}`);
2993
+ if (result.outcome === 'pass') failures.push(`${label}/stateful: a record bound to the later value passed`);
2994
+ if (!out.diagnostics.some(item => item.code === 'stale-artifact-digest')) {
2995
+ failures.push(`${label}/stateful: diagnostics=${out.diagnostics.map(item => item.code)}`);
2996
+ }
2997
+ }
2998
+
2999
+ // Stable controls: the validated destination binds its own value; a different value is stale.
3000
+ {
3001
+ const passing = runWith({ digest: RENAME_DEST_B }, RENAME_DEST_B);
3002
+ if (passing.results.find(item => item.obligationId === 'doc-change:doc-lint').outcome !== 'pass') {
3003
+ failures.push(`${label}/stable: the validated destination did not pass; diagnostics=${passing.diagnostics.map(item => item.code)}`);
3004
+ }
3005
+ const stale = runWith({ digest: RENAME_DEST_B }, RENAME_DEST_C);
3006
+ const staleResult = stale.results.find(item => item.obligationId === 'doc-change:doc-lint');
3007
+ if (staleResult.outcome !== 'none') failures.push(`${label}/stable: outcome=${staleResult.outcome}`);
3008
+ if (!stale.diagnostics.some(item => item.code === 'stale-artifact-digest')) {
3009
+ failures.push(`${label}/stable: diagnostics=${stale.diagnostics.map(item => item.code)}`);
3010
+ }
3011
+ }
3012
+
3013
+ // A throwing destination digest getter fails closed instead of escaping.
3014
+ {
3015
+ const throwingDestination = {
3016
+ get digest() {
3017
+ throw new Error('destination digest getter');
3018
+ }
3019
+ };
3020
+ let out;
3021
+ try {
3022
+ out = runWith(throwingDestination, RENAME_DEST_B);
3023
+ } catch (error) {
3024
+ failures.push(`${label}/throwing: escaped: ${error.message}`);
3025
+ continue;
3026
+ }
3027
+ const result = out.results.find(item => item.obligationId === 'doc-change:doc-lint');
3028
+ if (result.outcome === 'pass') failures.push(`${label}/throwing: a throwing destination produced a pass`);
3029
+ if (!out.diagnostics.some(item => item.code === 'candidate-artifacts-invalid')) {
3030
+ failures.push(`${label}/throwing: diagnostics=${out.diagnostics.map(item => item.code)}`);
3031
+ }
3032
+ }
3033
+ }
3034
+ assert.deepStrictEqual(failures, [],
3035
+ `the rename destination digest must be captured once: ${failures.join('; ')}`);
3036
+ });
3037
+
2690
3038
  helper.finish();
@@ -82,10 +82,16 @@ after validation, cannot mute the stale-baseline diagnostic, and cannot drift th
82
82
  Its first read must therefore be the value every consumer sees; a getter that starts returning a
83
83
  different or unusable value later is factored out entirely. A baseline per-path inspection that
84
84
  throws during validation is a `baseline-artifact-invalid` metadata error and fails the path
85
- closed. The audit mirror is likewise keyed by the **validated** path snapshot rather than a
86
- re-read of `baselinePaths[].path`, so a stateful `path` accessor is read exactly once and a
87
- passing result always describes the baseline identity that validated and bound the evidence.
88
- closed instead of escaping the checker.
85
+ closed instead of escaping the checker. The audit mirror is likewise keyed by the **validated**
86
+ path snapshot rather than a re-read of `baselinePaths[].path`, so a stateful `path` accessor is
87
+ read exactly once and a passing result always describes the baseline identity that validated and
88
+ bound the evidence.
89
+
90
+ The whole-baseline identity `baseline.digest` follows the same discipline: it is read exactly
91
+ once and that same scalar is validated, so a stateful value cannot be inspected as A while every
92
+ evidence comparison uses B. The rename destination's own digest is also read exactly once during
93
+ its validated snapshot, and the captured value drives the metadata cache, binding, the stale
94
+ diagnostics, and the audit identity.
89
95
 
90
96
  Per-path evidence must be carried explicitly. Inherited properties in `baseline.artifacts` or a
91
97
  record's `baselineArtifacts` are not own-key evidence and never satisfy a path; the trusted
@@ -110,6 +116,11 @@ candidate digest — the same one used by ordinary candidate binding, the audit
110
116
  stale diagnostics. The destination is therefore never read again through the candidate map during
111
117
  capture or binding: a nested `digest` that throws or changes after validation cannot make a stale
112
118
  destination pair bind while the audit/stale verdicts still describe the captured value. The same
119
+ read-once guarantee extends to rejected destination snapshots: when the destination's own digest
120
+ read throws or returns an unusable value, that failed snapshot ('') is recorded and all later
121
+ reads for that destination path are suppressed as well — the entry's single metadata read remains
122
+ its only digest read, and the failed path contributes `path=missing` to `artifactDigest` instead
123
+ of a later, different value. The same
113
124
  record can cover both endpoints. Destination-only evidence, source-only tombstones, split endpoint
114
125
  records, and stale destination digests do not satisfy the source. A deletion tombstone therefore
115
126
  cannot stand in for a known rename. This destination validation and the snapshot it produces are
@@ -129,14 +140,24 @@ obligations. `N/A` and `unknown` obligations never bind evidence and keep the le
129
140
  for their audit identity (so a candidate `get` that supplies a digest is still reflected there);
130
141
  they remain `outcome: 'none'` and do not emit `candidate-artifacts-invalid` from this applicability-gated check.
131
142
 
143
+ When several `rename-from` entries declare one shared destination, the read-once promise is scoped
144
+ **per declaring entry**: each entry's metadata validation reads the shared destination once (their
145
+ agreement of validated values is what detects a contradictory shared-destination snapshot). A
146
+ poisoned destination — one whose snapshot was rejected — is honored inside the metadata loop: a
147
+ later declaring entry fails closed without reading the destination again, and the failed snapshot
148
+ wins in the audit, overriding any previously validated successful snapshot that an earlier entry
149
+ carried, rather than silently describing a value the getter shipped after the failure.
150
+
132
151
  A rename destination's validated own digest is carried too. When more than one accepted
133
152
  `rename-from` entry shares one `renameTo`, every entry's validated own digest must agree; a
134
153
  conflicting validated digest is a contradictory shared-destination snapshot and fails the whole
135
154
  obligation closed (metadata error, `outcome: 'none'`) before any binding. The carried digest must
136
155
  be a non-empty string: a stateful `digest` accessor whose value is not a usable string is not
137
- carried or seeded. Only **applicable** obligations validate and carry this destination snapshot;
138
- `N/A` and `unknown` obligations keep the legacy candidate read for their audit identity and do not
139
- consume the destination getter during metadata validation.
156
+ carried or seeded — instead the destination path is recorded as a visited failure, and neither the
157
+ capture loop nor the audit identity reads the destination again. Only **applicable** obligations
158
+ validate and carry this destination snapshot; `N/A` and `unknown` obligations keep the legacy
159
+ candidate read for their audit identity and do not consume the destination getter during metadata
160
+ validation.
140
161
 
141
162
  The binding applies to execution and review records alike. Existing review approval, waiver,
142
163
  failing-command, requirement-sync authority, and applicability rules are unchanged. A correctly
@@ -4,7 +4,7 @@ description: "Route software/project work through Harness: classify tier/topolog
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.36.1
7
+ version: 0.36.2
8
8
  ---
9
9
 
10
10
  # Harness Everything
@@ -72,8 +72,18 @@ function artifact(entry) {
72
72
 
73
73
  // #335 P2: a baseline-bound rename destination must declare its own digest. An inherited
74
74
  // `digest` (for example Object.create({ digest })) is not explicit candidate evidence.
75
- function ownArtifactDigest(entry) {
76
- return isPlainObject(entry) && hasOwnEntry(entry, 'digest') ? artifact(entry) : '';
75
+ // P2(r4321077631): reads the own digest exactly once. `artifact()` inspects `entry.digest` up
76
+ // to three times (typeof, length, value), so a stateful accessor could validate one value and
77
+ // have the captured destination snapshot carry another. Only the applicable rename-destination
78
+ // snapshot uses this helper; non-baseline and inapplicable lanes keep the legacy `artifact()`
79
+ // reads for their audit identity. A throwing digest getter propagates to the caller's guarded
80
+ // block and fails the inspection closed there. This is the single authoritative own-digest
81
+ // helper; nothing else in this module reads a destination's own digest.
82
+ function ownDigestOnce(entry) {
83
+ if (!isPlainObject(entry)) return '';
84
+ if (!hasOwnEntry(entry, 'digest')) return '';
85
+ const digest = entry.digest;
86
+ return typeof digest === 'string' && digest.length > 0 ? digest : '';
77
87
  }
78
88
 
79
89
  function provenanceText(record) {
@@ -142,7 +152,20 @@ function checkDocObligations({ obligations, artifacts, baseline, executionRecord
142
152
  }
143
153
  return candidateInventoryKnown;
144
154
  };
145
- const baselineDigest = isPlainObject(baseline) && nonEmptyString(baseline.digest) ? baseline.digest : '';
155
+ // P2(r4321077638): the whole-baseline identity is read exactly once and that same scalar is
156
+ // validated, so a stateful getter cannot pass validation as A and have every later comparison
157
+ // use B. A baseline identity whose inspection throws is unusable input: the captured digest
158
+ // stays '', so baseline-bound evaluations fail closed through `baseline-identity-missing`
159
+ // instead of escaping the checker, and non-baseline callers keep the legacy '' shape.
160
+ let baselineDigest = '';
161
+ if (isPlainObject(baseline)) {
162
+ try {
163
+ const declaredBaselineDigest = baseline.digest;
164
+ if (nonEmptyString(declaredBaselineDigest)) baselineDigest = declaredBaselineDigest;
165
+ } catch {
166
+ baselineDigest = '';
167
+ }
168
+ }
146
169
  const executionList = Array.isArray(executionRecords) ? executionRecords.filter(isRecord) : [];
147
170
  const reviewList = Array.isArray(reviewRecords) ? reviewRecords.filter(isRecord) : [];
148
171
 
@@ -184,6 +207,16 @@ function evaluateObligation(obligation, context) {
184
207
  // #340: every accepted entry sharing a rename destination must validate the same own digest,
185
208
  // or the shared-destination snapshot is contradictory and the obligation fails closed.
186
209
  const validatedDestinationDigestMap = new Map();
210
+ // #342 P3: a rename destination whose own-digest read throws or returns an unusable value is
211
+ // still a visited snapshot. Its captured ('') value is recorded here so the ordinary capture
212
+ // and the audit identity never re-read that destination entry: the single metadata read stays
213
+ // the only digest read for that declaring entry, and a stateful getter cannot ship a later
214
+ // non-empty value into the audit key even though its entry was rejected. A poisoned
215
+ // destination is honored inside the metadata loop (a later declaring entry fails closed
216
+ // without reading it) and keeps precedence over a previously seeded successful snapshot;
217
+ // multiple accepted entries still read the destination once each by design, because their
218
+ // agreement check detects shared-destination contradictions.
219
+ const failedDestinationDigests = new Map();
187
220
  // #340 P2(r4320818749): the validated per-path baseline digest is captured once during
188
221
  // metadata validation. The audit mirror, the stale-baseline diagnostics, and baselineBound()
189
222
  // reuse that captured value instead of re-reading the caller map, so a stateful own getter
@@ -289,19 +322,38 @@ function evaluateObligation(obligation, context) {
289
322
  // keeps the un-gated legacy candidate read; only applicable obligations capture and
290
323
  // validate the destination snapshot.
291
324
  if (applicability === 'applicable') {
325
+ // #342 review P3: a destination poisoned by an earlier entry's failed snapshot is
326
+ // honored inside the metadata loop; the failed visited snapshot is never read again
327
+ // and this entry fails closed with it, so a stateful getter's later successful
328
+ // value cannot repair a shared destination or enter the audit.
329
+ if (failedDestinationDigests.has(entryRenameTo)) {
330
+ invalid(entryPath, `its rename destination ${JSON.stringify(entryRenameTo)} failed an earlier snapshot validation for this obligation; the failed destination is not read again and this entry fails closed with it`);
331
+ continue;
332
+ }
292
333
  let destinationDigest = '';
293
334
  try {
294
335
  if (hasOwnEntry(artifactMap, entryRenameTo)) {
295
- destinationDigest = ownArtifactDigest(artifactMap[entryRenameTo]);
336
+ // P2(r4321077631): read the destination's own digest exactly once; the captured
337
+ // value is the one validated below and the one that flows to the metadata cache,
338
+ // binding, the stale diagnostics, and the audit identity. A stateful destination
339
+ // getter cannot be validated as B while a record carrying only its later C passes.
340
+ destinationDigest = ownDigestOnce(artifactMap[entryRenameTo]);
296
341
  }
297
342
  } catch {
298
343
  candidateInventoryInspectionFailed = true;
344
+ // #342 P3: the destination is a visited snapshot even when its digest read threw;
345
+ // record it so the capture and audit lanes never re-read it.
346
+ failedDestinationDigests.set(entryRenameTo, '');
299
347
  continue;
300
348
  }
301
- // #340: the captured own value must be a usable non-empty string; `artifact()` can read
302
- // a stateful accessor three times, so a non-string result is not carried or seeded.
349
+ // #340: the captured own value must be a usable non-empty string; an unusable result
350
+ // is not carried or seeded.
303
351
  if (!nonEmptyString(destinationDigest)) {
304
352
  invalid(entryPath, `its rename destination ${JSON.stringify(entryRenameTo)} has no candidate artifact`);
353
+ // #342 P3: the destination is a visited snapshot even when its own digest is
354
+ // unusable; record it so the capture and audit lanes never re-read it and cannot
355
+ // describe a stateful getter's later non-empty value.
356
+ failedDestinationDigests.set(entryRenameTo, '');
305
357
  continue;
306
358
  }
307
359
  // #340: entries that share one rename destination must agree on the validated own
@@ -377,7 +429,9 @@ function evaluateObligation(obligation, context) {
377
429
  // its own digest during metadata. Seed the captured snapshot from that validated value and
378
430
  // never read the destination through the raw candidate map again, so a destination accessor
379
431
  // that throws or changes after validation cannot make capture/binding/audit disagree. The
380
- // seeded value is the validated own digest, never a caller-asserted rawEntry field.
432
+ // seeded value is the validated own digest, never a caller-asserted rawEntry field. A
433
+ // rejected destination is still visited: its failed capture (see failedDestinationDigests)
434
+ // suppresses the ordinary read below, so no destination entry is ever read twice.
381
435
  for (const entry of baselineEntries.values()) {
382
436
  if (entry.reason === 'rename-from' && nonEmptyString(entry.renameDestinationDigest)
383
437
  && !candidateDigestCache.has(entry.renameTo)) {
@@ -385,7 +439,8 @@ function evaluateObligation(obligation, context) {
385
439
  }
386
440
  }
387
441
  for (const path of scopePaths) {
388
- if (baselineEntries.has(path) || candidateDigestCache.has(path)) continue;
442
+ if (baselineEntries.has(path) || candidateDigestCache.has(path)
443
+ || failedDestinationDigests.has(path)) continue;
389
444
  try {
390
445
  candidateDigestCache.set(path, artifact(artifactMap[path]));
391
446
  } catch {
@@ -428,6 +483,11 @@ function evaluateObligation(obligation, context) {
428
483
  // stale verdict disagree with the digest that actually bound evidence. Untouched paths and
429
484
  // unannotated callers keep the legacy read.
430
485
  const candidateDigestOf = (path) => {
486
+ // #342 P3: poisoned state takes precedence over the capture cache. A rejected rename
487
+ // destination keeps its visited '' capture even when an earlier accepted entry seeded a
488
+ // successful value, so the audit never describes a value the getter shipped after a failed
489
+ // snapshot made the obligation fail closed; the caller map is never read for it again.
490
+ if (failedDestinationDigests.has(path)) return '';
431
491
  if (candidateDigestCache.has(path)) return candidateDigestCache.get(path);
432
492
  // #340: only an applicable baseline-annotated obligation validated the source absence as
433
493
  // part of its captured snapshot. `N/A` and `unknown` obligations never bind evidence, so they
@@ -509,7 +569,7 @@ function evaluateObligation(obligation, context) {
509
569
  // candidate lane, the audit identity, and the stale diagnostics use, so a stateful nested
510
570
  // accessor cannot make binding disagree with them. Metadata validation already required the
511
571
  // destination to be an own-key candidate inventory entry carrying its own digest
512
- // (ownArtifactDigest), so a non-empty captured digest is required here and the accessor is
572
+ // (ownDigestOnce), so a non-empty captured digest is required here and the accessor is
513
573
  // never re-read; a missing destination skips capture and lands on this guard.
514
574
  const capturedDestination = candidateDigestCache.get(entry.renameTo);
515
575
  if (!nonEmptyString(capturedDestination)) return false;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "harness-enforcement",
3
3
  "description": "Repository inventory for the opencode enforcement plugin and its standalone helper",
4
- "version": "0.36.1",
4
+ "version": "0.36.2",
5
5
  "runtime": "index.mjs",
6
6
  "hooks": {
7
7
  "verify": "hooks/verify.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-everything",
3
- "version": "0.36.1",
3
+ "version": "0.36.2",
4
4
  "description": "Harness OS: An Orchestrated Agent Operating System for AI-driven Development",
5
5
  "main": "harness-everything/scripts/bootstrap.js",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-everything",
3
- "version": "0.36.1",
3
+ "version": "0.36.2",
4
4
  "description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
5
5
  "author": {
6
6
  "name": "Miya Daniel",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "harness-everything",
4
- "version": "0.36.1",
4
+ "version": "0.36.2",
5
5
  "description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
6
6
  "author": {
7
7
  "name": "Miya Daniel",
@@ -4,7 +4,7 @@ description: "Route software/project work through Harness: classify tier/topolog
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.36.1
7
+ version: 0.36.2
8
8
  ---
9
9
 
10
10
  # Harness Everything
@@ -72,8 +72,18 @@ function artifact(entry) {
72
72
 
73
73
  // #335 P2: a baseline-bound rename destination must declare its own digest. An inherited
74
74
  // `digest` (for example Object.create({ digest })) is not explicit candidate evidence.
75
- function ownArtifactDigest(entry) {
76
- return isPlainObject(entry) && hasOwnEntry(entry, 'digest') ? artifact(entry) : '';
75
+ // P2(r4321077631): reads the own digest exactly once. `artifact()` inspects `entry.digest` up
76
+ // to three times (typeof, length, value), so a stateful accessor could validate one value and
77
+ // have the captured destination snapshot carry another. Only the applicable rename-destination
78
+ // snapshot uses this helper; non-baseline and inapplicable lanes keep the legacy `artifact()`
79
+ // reads for their audit identity. A throwing digest getter propagates to the caller's guarded
80
+ // block and fails the inspection closed there. This is the single authoritative own-digest
81
+ // helper; nothing else in this module reads a destination's own digest.
82
+ function ownDigestOnce(entry) {
83
+ if (!isPlainObject(entry)) return '';
84
+ if (!hasOwnEntry(entry, 'digest')) return '';
85
+ const digest = entry.digest;
86
+ return typeof digest === 'string' && digest.length > 0 ? digest : '';
77
87
  }
78
88
 
79
89
  function provenanceText(record) {
@@ -142,7 +152,20 @@ function checkDocObligations({ obligations, artifacts, baseline, executionRecord
142
152
  }
143
153
  return candidateInventoryKnown;
144
154
  };
145
- const baselineDigest = isPlainObject(baseline) && nonEmptyString(baseline.digest) ? baseline.digest : '';
155
+ // P2(r4321077638): the whole-baseline identity is read exactly once and that same scalar is
156
+ // validated, so a stateful getter cannot pass validation as A and have every later comparison
157
+ // use B. A baseline identity whose inspection throws is unusable input: the captured digest
158
+ // stays '', so baseline-bound evaluations fail closed through `baseline-identity-missing`
159
+ // instead of escaping the checker, and non-baseline callers keep the legacy '' shape.
160
+ let baselineDigest = '';
161
+ if (isPlainObject(baseline)) {
162
+ try {
163
+ const declaredBaselineDigest = baseline.digest;
164
+ if (nonEmptyString(declaredBaselineDigest)) baselineDigest = declaredBaselineDigest;
165
+ } catch {
166
+ baselineDigest = '';
167
+ }
168
+ }
146
169
  const executionList = Array.isArray(executionRecords) ? executionRecords.filter(isRecord) : [];
147
170
  const reviewList = Array.isArray(reviewRecords) ? reviewRecords.filter(isRecord) : [];
148
171
 
@@ -184,6 +207,16 @@ function evaluateObligation(obligation, context) {
184
207
  // #340: every accepted entry sharing a rename destination must validate the same own digest,
185
208
  // or the shared-destination snapshot is contradictory and the obligation fails closed.
186
209
  const validatedDestinationDigestMap = new Map();
210
+ // #342 P3: a rename destination whose own-digest read throws or returns an unusable value is
211
+ // still a visited snapshot. Its captured ('') value is recorded here so the ordinary capture
212
+ // and the audit identity never re-read that destination entry: the single metadata read stays
213
+ // the only digest read for that declaring entry, and a stateful getter cannot ship a later
214
+ // non-empty value into the audit key even though its entry was rejected. A poisoned
215
+ // destination is honored inside the metadata loop (a later declaring entry fails closed
216
+ // without reading it) and keeps precedence over a previously seeded successful snapshot;
217
+ // multiple accepted entries still read the destination once each by design, because their
218
+ // agreement check detects shared-destination contradictions.
219
+ const failedDestinationDigests = new Map();
187
220
  // #340 P2(r4320818749): the validated per-path baseline digest is captured once during
188
221
  // metadata validation. The audit mirror, the stale-baseline diagnostics, and baselineBound()
189
222
  // reuse that captured value instead of re-reading the caller map, so a stateful own getter
@@ -289,19 +322,38 @@ function evaluateObligation(obligation, context) {
289
322
  // keeps the un-gated legacy candidate read; only applicable obligations capture and
290
323
  // validate the destination snapshot.
291
324
  if (applicability === 'applicable') {
325
+ // #342 review P3: a destination poisoned by an earlier entry's failed snapshot is
326
+ // honored inside the metadata loop; the failed visited snapshot is never read again
327
+ // and this entry fails closed with it, so a stateful getter's later successful
328
+ // value cannot repair a shared destination or enter the audit.
329
+ if (failedDestinationDigests.has(entryRenameTo)) {
330
+ invalid(entryPath, `its rename destination ${JSON.stringify(entryRenameTo)} failed an earlier snapshot validation for this obligation; the failed destination is not read again and this entry fails closed with it`);
331
+ continue;
332
+ }
292
333
  let destinationDigest = '';
293
334
  try {
294
335
  if (hasOwnEntry(artifactMap, entryRenameTo)) {
295
- destinationDigest = ownArtifactDigest(artifactMap[entryRenameTo]);
336
+ // P2(r4321077631): read the destination's own digest exactly once; the captured
337
+ // value is the one validated below and the one that flows to the metadata cache,
338
+ // binding, the stale diagnostics, and the audit identity. A stateful destination
339
+ // getter cannot be validated as B while a record carrying only its later C passes.
340
+ destinationDigest = ownDigestOnce(artifactMap[entryRenameTo]);
296
341
  }
297
342
  } catch {
298
343
  candidateInventoryInspectionFailed = true;
344
+ // #342 P3: the destination is a visited snapshot even when its digest read threw;
345
+ // record it so the capture and audit lanes never re-read it.
346
+ failedDestinationDigests.set(entryRenameTo, '');
299
347
  continue;
300
348
  }
301
- // #340: the captured own value must be a usable non-empty string; `artifact()` can read
302
- // a stateful accessor three times, so a non-string result is not carried or seeded.
349
+ // #340: the captured own value must be a usable non-empty string; an unusable result
350
+ // is not carried or seeded.
303
351
  if (!nonEmptyString(destinationDigest)) {
304
352
  invalid(entryPath, `its rename destination ${JSON.stringify(entryRenameTo)} has no candidate artifact`);
353
+ // #342 P3: the destination is a visited snapshot even when its own digest is
354
+ // unusable; record it so the capture and audit lanes never re-read it and cannot
355
+ // describe a stateful getter's later non-empty value.
356
+ failedDestinationDigests.set(entryRenameTo, '');
305
357
  continue;
306
358
  }
307
359
  // #340: entries that share one rename destination must agree on the validated own
@@ -377,7 +429,9 @@ function evaluateObligation(obligation, context) {
377
429
  // its own digest during metadata. Seed the captured snapshot from that validated value and
378
430
  // never read the destination through the raw candidate map again, so a destination accessor
379
431
  // that throws or changes after validation cannot make capture/binding/audit disagree. The
380
- // seeded value is the validated own digest, never a caller-asserted rawEntry field.
432
+ // seeded value is the validated own digest, never a caller-asserted rawEntry field. A
433
+ // rejected destination is still visited: its failed capture (see failedDestinationDigests)
434
+ // suppresses the ordinary read below, so no destination entry is ever read twice.
381
435
  for (const entry of baselineEntries.values()) {
382
436
  if (entry.reason === 'rename-from' && nonEmptyString(entry.renameDestinationDigest)
383
437
  && !candidateDigestCache.has(entry.renameTo)) {
@@ -385,7 +439,8 @@ function evaluateObligation(obligation, context) {
385
439
  }
386
440
  }
387
441
  for (const path of scopePaths) {
388
- if (baselineEntries.has(path) || candidateDigestCache.has(path)) continue;
442
+ if (baselineEntries.has(path) || candidateDigestCache.has(path)
443
+ || failedDestinationDigests.has(path)) continue;
389
444
  try {
390
445
  candidateDigestCache.set(path, artifact(artifactMap[path]));
391
446
  } catch {
@@ -428,6 +483,11 @@ function evaluateObligation(obligation, context) {
428
483
  // stale verdict disagree with the digest that actually bound evidence. Untouched paths and
429
484
  // unannotated callers keep the legacy read.
430
485
  const candidateDigestOf = (path) => {
486
+ // #342 P3: poisoned state takes precedence over the capture cache. A rejected rename
487
+ // destination keeps its visited '' capture even when an earlier accepted entry seeded a
488
+ // successful value, so the audit never describes a value the getter shipped after a failed
489
+ // snapshot made the obligation fail closed; the caller map is never read for it again.
490
+ if (failedDestinationDigests.has(path)) return '';
431
491
  if (candidateDigestCache.has(path)) return candidateDigestCache.get(path);
432
492
  // #340: only an applicable baseline-annotated obligation validated the source absence as
433
493
  // part of its captured snapshot. `N/A` and `unknown` obligations never bind evidence, so they
@@ -509,7 +569,7 @@ function evaluateObligation(obligation, context) {
509
569
  // candidate lane, the audit identity, and the stale diagnostics use, so a stateful nested
510
570
  // accessor cannot make binding disagree with them. Metadata validation already required the
511
571
  // destination to be an own-key candidate inventory entry carrying its own digest
512
- // (ownArtifactDigest), so a non-empty captured digest is required here and the accessor is
572
+ // (ownDigestOnce), so a non-empty captured digest is required here and the accessor is
513
573
  // never re-read; a missing destination skips capture and lands on this guard.
514
574
  const capturedDestination = candidateDigestCache.get(entry.renameTo);
515
575
  if (!nonEmptyString(capturedDestination)) return false;
package/sbom.cdx.json CHANGED
@@ -2,23 +2,23 @@
2
2
  "$schema": "https://cyclonedx.org/schema/bom-1.7.schema.json",
3
3
  "bomFormat": "CycloneDX",
4
4
  "specVersion": "1.7",
5
- "serialNumber": "urn:uuid:74100c9d-ebbb-559a-8ab0-a9ac5fd581c6",
5
+ "serialNumber": "urn:uuid:d784e4c6-dc73-508d-9e04-a38908e96486",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-10-20T03:51:47.041Z",
8
+ "timestamp": "2026-10-20T05:31:16.491Z",
9
9
  "tools": [
10
10
  {
11
11
  "vendor": "Harness Everything",
12
12
  "name": "generate-sbom.js",
13
- "version": "0.36.1"
13
+ "version": "0.36.2"
14
14
  }
15
15
  ],
16
16
  "component": {
17
17
  "type": "application",
18
- "bom-ref": "pkg:npm/harness-everything@0.36.1",
18
+ "bom-ref": "pkg:npm/harness-everything@0.36.2",
19
19
  "name": "harness-everything",
20
- "version": "0.36.1",
21
- "purl": "pkg:npm/harness-everything@0.36.1"
20
+ "version": "0.36.2",
21
+ "purl": "pkg:npm/harness-everything@0.36.2"
22
22
  }
23
23
  },
24
24
  "components": [
@@ -1999,7 +1999,7 @@
1999
1999
  ]
2000
2000
  },
2001
2001
  {
2002
- "ref": "pkg:npm/harness-everything@0.36.1",
2002
+ "ref": "pkg:npm/harness-everything@0.36.2",
2003
2003
  "dependsOn": [
2004
2004
  "pkg:npm/%40commitlint/cli@19.8.1",
2005
2005
  "pkg:npm/%40commitlint/config-conventional@19.8.1",