[GH-ISSUE #3363] [Bug] Ready obligation ledgers retain missing manifest Evidence references #1229

Closed
opened 2026-10-16 21:03:04 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3363

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

rea build-reconstruction-obligation-ledger reports a required obligation as verified and its ledger as ready when the manifest retains an original-case or fixture Evidence ID absent from the input bundle. One valid entry for the same case kind hides the missing reference. The output includes the absent ID in its Evidence links without a diagnostic.

All three CLI invocations in the reproduction succeeded. Both missing-reference variants report ready, mark the obligation verified, retain no diagnostics, and include the nonexistent Evidence ID in the output links. The complete control reports ready without a dangling link.

Reproduction

  1. Start from a clean checkout of morluto/rea at 8dde6370d0809ad04cda3342d4e6f290f48aa063. Install the locked dependencies and build the runtime from the repository root:
npm ci
npm run build:cached
  1. Run the following complete fixture from the repository root. It creates a controlled static claim and proof record, establishes a ready control, then separately adds one missing fixture reference and one missing original-case reference. Unicode escapes in JavaScript identifiers and strings preserve their ordinary runtime values.
export REPRO_DIR="${REPRO_DIR:-$(mktemp -d)}"
mkdir -p "$REPRO_DIR/home"
node --input-type=module - <<'JS'
import fs from 'node:fs';
import { createEvidence } from './dist/domain/evidence.js';
import { createEvidenceBundle } from './dist/domain/evidenceBundle.js';
const id = 'cli.sample';
const proof = createEvidence(undefined,
  { id: 'sample-ver\u0069fier', name: 'Sample ver\u0069fier', version: '1' }, {
    predicateType: 'rea.reconstruction-proof',
    operation: 'verify_reconstruction_obligations', parameters: {},
    result: { passed: true, obligation_ids: [id],
      fixture_ids: ['sample.positive'], case_kinds: ['positive'],
      ver\u0069fier_ids: ['sample.ver\u0069fier'], claim_ids: ['sample.claim'] },
    confidence: 'observed', authority: 'shipped-artifact'
  });
const reviewed = {
  obligation_id: id, obligation_version: 1, title: 'Sample static claim',
  application_layer: 'other', family: 'sample',
  target: { artifact_sha256: null, application_node_id: null,
    semantic_node_id: null, location: 'sample' },
  required: true, required_case_kinds: ['positive'],
  required_original_authority: 'static', required_fixture_authority: 'unit',
  required_ver\u0069fier_authority: 'unit', requires_parser_type: false,
  dependency_obligation_ids: [], residual_unknown_ids: [],
  unavailable_authority: [], required_next_evidence: [],
  disposition: 'active', review_evidence_ids: [proof.evidence_id]
};
const binding = {
  obligation_id: id,
  owner: { module_path: 'sample.ts', symbol: 'sample',
    owner_sha256: '1'.repeat(64) }, parser_type: null,
  original_cases: [{ kind: 'positive', evidence_id: proof.evidence_id,
    location: '/positive' }],
  fixtures: [{ fixture_id: 'sample.positive', case_kind: 'positive',
    authority: 'unit', evidence_ids: [proof.evidence_id] }],
  ver\u0069fier: { ver\u0069fier_id: 'sample.ver\u0069fier', claim_id: 'sample.claim',
    command: 'node sample-ver\u0069fier.mjs', authority: 'unit', status: 'pass',
    result_evidence_id: proof.evidence_id, enumerated_obligation_ids: [id],
    nondeterminism: { mode: 'total-order', specification: 'One static assertion.' } }
};
for (const variant of ['control', 'missing-fixture', 'missing-original']) {
  const b = structuredClone(binding);
  const missing = 'ev_' + 'a'.repeat(64);
  if (variant === 'missing-fixture') b.fixtures.push({
    fixture_id: 'sample.old', case_kind: 'positive', authority: 'unit',
    evidence_ids: [missing] });
  if (variant === 'missing-original') b.original_cases.push({
    kind: 'positive', evidence_id: missing, location: '/old-positive' });
  fs.writeFileSync(`${process.env.REPRO_DIR}/${variant}.json`, JSON.stringify({
    evidence_bundle: createEvidenceBundle([proof]), reviewed_obligations: [reviewed],
    manifest: { bindings: [b], contradictions: [] } }));
}
JS
for variant in control missing-fixture missing-original; do
  HOME="$REPRO_DIR/home" node scripts/rea.mjs build-reconstruction-obligation-ledger \
    "$REPRO_DIR/$variant.json" --json > "$REPRO_DIR/$variant-ledger.json"
done
node --input-type=module - <<'JS'
import fs from 'node:fs';
for (const variant of ['control', 'missing-fixture', 'missing-original']) {
  const l = JSON.parse(fs.readFileSync(
    `${process.env.REPRO_DIR}/${variant}-ledger.json`)).normalized_result;
  console.log(variant, JSON.stringify({ status: l.status,
    obligation_status: l.obligations[0].status,
    diagnostics: l.obligations[0].diagnostics,
    dangling_link: l.evidence_links.includes('ev_' + 'a'.repeat(64)) }));
}
JS
  1. Compare the control with missing-fixture and missing-original. Both missing-reference variants return ready and verified with empty diagnostics and a dangling Evidence link, as shown below.

Expected behavior

docs/reconstruction-obligation-ledgers.md:318 requires all referenced Evidence IDs to exist in the bundle. Its Manifest and closure section also requires authenticated original cases and reconstruction fixtures.

The control must remain ready. Each variant with an absent Evidence ID must fail validation or retain an explicit unresolved-reference diagnostic and withhold readiness. Finding a valid record for another entry of the same case kind does not satisfy the missing entry's reference.

Impact and scope

P2, supported. A valid proof plus a stale manifest reference produces a ready result with incomplete provenance and no warning. This is a bounded analysis-result defect; the reproduction does not establish loss of stored Evidence.

A workaround is available. Compare every manifest reference against the input bundle and remove obsolete references or restore their records before building the ledger. The control demonstrates that a request without dangling references remains ready.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1. Architecture was not recorded. No external analysis engine is required.

Target and artifact context

A synthetic authenticated Evidence bundle containing one shipped-artifact proof record, one reviewed required static obligation with a positive case, and a reconstruction manifest. Each failing variant retains the valid positive entry and adds another entry of the same case kind referencing ev_ followed by 64 a characters, which is absent from the bundle. The fixture variant adds sample.old; the original-case variant adds /old-positive. The input files and isolated CLI home are created by the inline reproduction.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

control {"status":"ready","obligation_status":"verified","diagnostics":[],"dangling_link":false}
missing-fixture {"status":"ready","obligation_status":"verified","diagnostics":[],"dangling_link":true}
missing-original {"status":"ready","obligation_status":"verified","diagnostics":[],"dangling_link":true}

Typed outcome or recovery expected

Run the control and both missing-reference variants. The control must remain ready. Each missing-reference variant must identify the absent record and withhold readiness or reject the request. Removing the missing entry must restore the control result. Also retain detection when the missing ID is the only supplied original observation or fixture proof.

Analysis or suspected cause

Source-derived reasoning: src/application/ReconstructionObligationLedgerEvaluation.ts:272 accepts any qualifying fixture for each required kind, and src/application/ReconstructionObligationOriginalCases.ts:49 does the same for original observations. Missing IDs in the other retained entries receive no diagnostic. The per-kind existence checks explain how one valid entry can hide an absent record in another retained entry; they do not enforce the documented all-references requirement.

The failure evidence is the reproduced public CLI output above. Evidence provenance is observed, local verification reproduced the behavior, and the reproduction includes the complete input construction.

Additional context

  • Related #3219 concerns error classification for a non-canonical bundle; this report concerns an authenticated bundle whose manifest references absent records and still receives a ready ledger.
  • Related #3005 concerns reaching verified status through MCP proof registration and updating unknown records; this report concerns incorrect readiness with missing retained references through the CLI.
  • Related completed #2178 concerns duplicate missing-Evidence diagnostics in the internal readiness evaluator, where readiness correctly remains unknown; this report concerns absent diagnostics and false readiness in the public obligation ledger.
  • Related completed #405 introduced obligation-ledger closure and authenticated proof requirements; this report isolates a missing-reference validation gap in that feature.
  • Related completed #268 defines the broader evidence-backed coverage workspace; this report concerns manifest references in the obligation-ledger command.
  • Related completed #72 concerns generated Evidence indexes and completion summaries; this report concerns validation of retained manifest references during ledger evaluation.
  • Related completed #406 concerns the end-to-end reconstruction readiness suite; this report provides a focused CLI fixture for a manifest-reference closure failure.
  • Related completed #796 concerns omitted semantic unknowns during candidate generation; this report concerns missing Evidence records referenced by retained manifest entries.
Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3363 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `rea build-reconstruction-obligation-ledger` reports a required obligation as `verified` and its ledger as `ready` when the manifest retains an original-case or fixture Evidence ID absent from the input bundle. One valid entry for the same case kind hides the missing reference. The output includes the absent ID in its Evidence links without a diagnostic. All three CLI invocations in the reproduction succeeded. Both missing-reference variants report `ready`, mark the obligation `verified`, retain no diagnostics, and include the nonexistent Evidence ID in the output links. The complete control reports `ready` without a dangling link. ### Reproduction 1. Start from a clean checkout of `morluto/rea` at `8dde6370d0809ad04cda3342d4e6f290f48aa063`. Install the locked dependencies and build the runtime from the repository root: ```sh npm ci npm run build:cached ``` 2. Run the following complete fixture from the repository root. It creates a controlled static claim and proof record, establishes a ready control, then separately adds one missing fixture reference and one missing original-case reference. Unicode escapes in JavaScript identifiers and strings preserve their ordinary runtime values. ```sh export REPRO_DIR="${REPRO_DIR:-$(mktemp -d)}" mkdir -p "$REPRO_DIR/home" node --input-type=module - <<'JS' import fs from 'node:fs'; import { createEvidence } from './dist/domain/evidence.js'; import { createEvidenceBundle } from './dist/domain/evidenceBundle.js'; const id = 'cli.sample'; const proof = createEvidence(undefined, { id: 'sample-ver\u0069fier', name: 'Sample ver\u0069fier', version: '1' }, { predicateType: 'rea.reconstruction-proof', operation: 'verify_reconstruction_obligations', parameters: {}, result: { passed: true, obligation_ids: [id], fixture_ids: ['sample.positive'], case_kinds: ['positive'], ver\u0069fier_ids: ['sample.ver\u0069fier'], claim_ids: ['sample.claim'] }, confidence: 'observed', authority: 'shipped-artifact' }); const reviewed = { obligation_id: id, obligation_version: 1, title: 'Sample static claim', application_layer: 'other', family: 'sample', target: { artifact_sha256: null, application_node_id: null, semantic_node_id: null, location: 'sample' }, required: true, required_case_kinds: ['positive'], required_original_authority: 'static', required_fixture_authority: 'unit', required_ver\u0069fier_authority: 'unit', requires_parser_type: false, dependency_obligation_ids: [], residual_unknown_ids: [], unavailable_authority: [], required_next_evidence: [], disposition: 'active', review_evidence_ids: [proof.evidence_id] }; const binding = { obligation_id: id, owner: { module_path: 'sample.ts', symbol: 'sample', owner_sha256: '1'.repeat(64) }, parser_type: null, original_cases: [{ kind: 'positive', evidence_id: proof.evidence_id, location: '/positive' }], fixtures: [{ fixture_id: 'sample.positive', case_kind: 'positive', authority: 'unit', evidence_ids: [proof.evidence_id] }], ver\u0069fier: { ver\u0069fier_id: 'sample.ver\u0069fier', claim_id: 'sample.claim', command: 'node sample-ver\u0069fier.mjs', authority: 'unit', status: 'pass', result_evidence_id: proof.evidence_id, enumerated_obligation_ids: [id], nondeterminism: { mode: 'total-order', specification: 'One static assertion.' } } }; for (const variant of ['control', 'missing-fixture', 'missing-original']) { const b = structuredClone(binding); const missing = 'ev_' + 'a'.repeat(64); if (variant === 'missing-fixture') b.fixtures.push({ fixture_id: 'sample.old', case_kind: 'positive', authority: 'unit', evidence_ids: [missing] }); if (variant === 'missing-original') b.original_cases.push({ kind: 'positive', evidence_id: missing, location: '/old-positive' }); fs.writeFileSync(`${process.env.REPRO_DIR}/${variant}.json`, JSON.stringify({ evidence_bundle: createEvidenceBundle([proof]), reviewed_obligations: [reviewed], manifest: { bindings: [b], contradictions: [] } })); } JS for variant in control missing-fixture missing-original; do HOME="$REPRO_DIR/home" node scripts/rea.mjs build-reconstruction-obligation-ledger \ "$REPRO_DIR/$variant.json" --json > "$REPRO_DIR/$variant-ledger.json" done node --input-type=module - <<'JS' import fs from 'node:fs'; for (const variant of ['control', 'missing-fixture', 'missing-original']) { const l = JSON.parse(fs.readFileSync( `${process.env.REPRO_DIR}/${variant}-ledger.json`)).normalized_result; console.log(variant, JSON.stringify({ status: l.status, obligation_status: l.obligations[0].status, diagnostics: l.obligations[0].diagnostics, dangling_link: l.evidence_links.includes('ev_' + 'a'.repeat(64)) })); } JS ``` 3. Compare the control with `missing-fixture` and `missing-original`. Both missing-reference variants return ready and verified with empty diagnostics and a dangling Evidence link, as shown below. ### Expected behavior `docs/reconstruction-obligation-ledgers.md:318` requires all referenced Evidence IDs to exist in the bundle. Its Manifest and closure section also requires authenticated original cases and reconstruction fixtures. The control must remain ready. Each variant with an absent Evidence ID must fail validation or retain an explicit unresolved-reference diagnostic and withhold readiness. Finding a valid record for another entry of the same case kind does not satisfy the missing entry's reference. ### Impact and scope P2, supported. A valid proof plus a stale manifest reference produces a ready result with incomplete provenance and no warning. This is a bounded analysis-result defect; the reproduction does not establish loss of stored Evidence. A workaround is available. Compare every manifest reference against the input bundle and remove obsolete references or restore their records before building the ledger. The control demonstrates that a request without dangling references remains ready. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1. Architecture was not recorded. No external analysis engine is required. ### Target and artifact context A synthetic authenticated Evidence bundle containing one shipped-artifact proof record, one reviewed required static obligation with a positive case, and a reconstruction manifest. Each failing variant retains the valid positive entry and adds another entry of the same case kind referencing `ev_` followed by 64 `a` characters, which is absent from the bundle. The fixture variant adds `sample.old`; the original-case variant adds `/old-positive`. The input files and isolated CLI home are created by the inline reproduction. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence ```text control {"status":"ready","obligation_status":"verified","diagnostics":[],"dangling_link":false} missing-fixture {"status":"ready","obligation_status":"verified","diagnostics":[],"dangling_link":true} missing-original {"status":"ready","obligation_status":"verified","diagnostics":[],"dangling_link":true} ``` ### Typed outcome or recovery expected Run the control and both missing-reference variants. The control must remain ready. Each missing-reference variant must identify the absent record and withhold readiness or reject the request. Removing the missing entry must restore the control result. Also retain detection when the missing ID is the only supplied original observation or fixture proof. ### Analysis or suspected cause Source-derived reasoning: `src/application/ReconstructionObligationLedgerEvaluation.ts:272` accepts any qualifying fixture for each required kind, and `src/application/ReconstructionObligationOriginalCases.ts:49` does the same for original observations. Missing IDs in the other retained entries receive no diagnostic. The per-kind existence checks explain how one valid entry can hide an absent record in another retained entry; they do not enforce the documented all-references requirement. The failure evidence is the reproduced public CLI output above. Evidence provenance is observed, local verification reproduced the behavior, and the reproduction includes the complete input construction. ### Additional context - Related [#3219](https://github.com/morluto/rea/issues/3219) concerns error classification for a non-canonical bundle; this report concerns an authenticated bundle whose manifest references absent records and still receives a ready ledger. - Related [#3005](https://github.com/morluto/rea/issues/3005) concerns reaching verified status through MCP proof registration and updating unknown records; this report concerns incorrect readiness with missing retained references through the CLI. - Related completed [#2178](https://github.com/morluto/rea/issues/2178) concerns duplicate missing-Evidence diagnostics in the internal readiness evaluator, where readiness correctly remains unknown; this report concerns absent diagnostics and false readiness in the public obligation ledger. - Related completed [#405](https://github.com/morluto/rea/issues/405) introduced obligation-ledger closure and authenticated proof requirements; this report isolates a missing-reference validation gap in that feature. - Related completed [#268](https://github.com/morluto/rea/issues/268) defines the broader evidence-backed coverage workspace; this report concerns manifest references in the obligation-ledger command. - Related completed [#72](https://github.com/morluto/rea/issues/72) concerns generated Evidence indexes and completion summaries; this report concerns validation of retained manifest references during ledger evaluation. - Related completed [#406](https://github.com/morluto/rea/issues/406) concerns the end-to-end reconstruction readiness suite; this report provides a focused CLI fixture for a manifest-reference closure failure. - Related completed [#796](https://github.com/morluto/rea/issues/796) concerns omitted semantic unknowns during candidate generation; this report concerns missing Evidence records referenced by retained manifest entries.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1229
No description provided.