[GH-ISSUE #5838] [Bug] Obligation ledger authority summary counts source states instead of evidence authorities #3535

Closed
opened 2026-10-31 00:41:37 +00:00 by bsakaguchi · 2 comments
Owner

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

Originally assigned to: @kaoru0822-kitauji on GitHub.

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

Two public CLI calls to node scripts/rea.mjs build-reconstruction-obligation-ledger <JSON> --json succeeded with authenticated synthetic Evidence and one reviewed obligation each. One input used shipped-artifact authority; the other used controlled-replay. Each output preserved the corresponding authority in obligations[0].authority_references[0].authority, but both returned the same summary.by_evidence_authority: candidate: 0, observed: 0, reviewed: 1, unknown: 0.

The summary groups the obligation's source_state. It cannot answer which evidence authority supports even this single-reference obligation. In the shipped-artifact case, the reference state was candidate, while the obligation source state and populated summary bucket were both reviewed.

Reproduction

The runtime and dependencies were already built and installed from the baseline. The following temporary service test executed two real CLI subprocesses; neither subprocess was mocked. It uses the repository's existing reviewed-obligation fixture and authenticated Evidence constructors.

  1. Save this test as src/application/repro.test.ts in the baseline checkout:
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { expect, it } from "vitest";
import { createEvidence } from "../domain/evidence.js";
import { createEvidenceBundle } from "../domain/evidenceBundle.js";
import { reviewedObligation } from "./ReconstructionObligationLedger.fixture.js";

const execute = promisify(execFile);

it("compares authority summaries through the public CLI", async () => {
  const summaries = [];
  for (const authority of ["shipped-artifact", "controlled-replay"] as const) {
    const evidence = createEvidence(undefined,
      { id: "fixture", name: "Fixture", version: "1" },
      { operation: "observe", parameters: {}, result: { present: true },
        confidence: "observed", authority });
    const input = {
      evidence_bundle: createEvidenceBundle([evidence]),
      reviewed_obligations: [reviewedObligation("review.one", evidence.evidence_id)],
      manifest: { bindings: [], contradictions: [] },
    };
    const { stdout } = await execute(process.execPath,
      ["scripts/rea.mjs", "build-reconstruction-obligation-ledger", JSON.stringify(input), "--json"],
      { cwd: process.cwd(), env: process.env, maxBuffer: 16 * 1024 * 1024 });
    const ledger = JSON.parse(stdout).normalized_result;
    expect(ledger.obligations).toHaveLength(1);
    expect(ledger.obligations[0].authority_references[0].authority).toBe(authority);
    summaries.push(ledger.summary.by_evidence_authority);
    console.log(JSON.stringify({ authority,
      reference_state: ledger.obligations[0].authority_references[0].state,
      source_state: ledger.obligations[0].source_state,
      by_evidence_authority: ledger.summary.by_evidence_authority }));
  }
  expect(summaries[0]).toEqual(summaries[1]);
  expect(summaries[0]).toContainEqual({ key: "shipped-artifact", count: 1 });
}, 20_000);

  1. Run the focused source test with an isolated home and XDG configuration. Set REPRO_DIR to a disposable directory before running:
mkdir -p "$REPRO_DIR/home" "$REPRO_DIR/config" "$REPRO_DIR/cache" "$REPRO_DIR/data"
HOME="$REPRO_DIR/home" XDG_CONFIG_HOME="$REPRO_DIR/config" \
XDG_CACHE_HOME="$REPRO_DIR/cache" XDG_DATA_HOME="$REPRO_DIR/data" \
npm run test:local -- src/application/repro.test.ts
  1. Both CLI calls return successfully, the authority-preservation assertions pass, and the summary-equality assertion passes. The final assertion fails because there is no shipped-artifact bucket. Delete the temporary test afterward.

This reproduced in one executed test containing both authority cases. No real provider or target is needed.

Expected behavior

An evidence-authority summary should distinguish the authority values carried by its obligations, rather than label a source-state histogram as an authority breakdown. For a single obligation with one shipped-artifact reference, an authority grouping should account for shipped-artifact; the corresponding controlled-replay input should account for controlled-replay.

At the baseline, the obligation reference schema explicitly separates authority from state. The coverage guide likewise distinguishes Evidence source authority from confidence. The originating ledger feature request specifies a summary grouped by application layer and evidence authority.

If a source-state histogram is also intended, its name should identify that dimension. The desired counting policy for obligations with multiple authority references is not established by this reproduction; these inputs each have exactly one reference.

Impact and scope

P2: the authority summary for a supported ledger request returns a different dimension, preventing consumers from obtaining the promised authority breakdown from that summary. A practical workaround is available: read and aggregate obligations[].authority_references[].authority; the reproduction confirmed those values remain intact inline.

The selector and schema make this a general grouping mismatch, although runtime verification covered only the two single-obligation CLI cases above. No incorrect readiness decision, lost Evidence, native-provider failure, or security boundary crossing was demonstrated. MCP execution and client views were not tested.

REA version

rea-agents 6.7.0; source baseline a5f982de8d

Runtime and platform

Node.js v26.12.0; macOS 27.0.1; Darwin arm64; built CLI with preinstalled locked dependencies; no external provider.

Target and artifact context

Synthetic authenticated Evidence bundle with one caller-reviewed obligation, one authority reference, and an empty reconstruction manifest; no native target.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

Trimmed stdout from the executed test, which parses the actual CLI responses:

{"authority":"shipped-artifact","reference_state":"candidate","source_state":"reviewed","by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]}
{"authority":"controlled-replay","reference_state":"reviewed","source_state":"reviewed","by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]}

The focused test then reported:

AssertionError: expected [ …(4) ] to deep equally contain { key: 'shipped-artifact', count: 1 }
Test Files  1 failed (1)
Tests  1 failed (1)

Source checks explain this output: the summary selector reads source_state; the summary schema restricts bucket keys to the four states. For reviewed input, candidate derivation preserves each record's authority but fixes the obligation source state to reviewed.

Typed outcome or recovery expected

No response

Analysis or suspected cause

Source-derived: the selector and output schema consistently implement a source-state histogram under an authority-summary name. Both need to remain consistent with any corrected contract. The closure digest includes the summary, so a corrected summary will also change the resulting digest; the existing digest does not expose the semantic mismatch.

Additional context

No response

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5838 Originally assigned to: @kaoru0822-kitauji on GitHub. ### 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 Two public CLI calls to `node scripts/rea.mjs build-reconstruction-obligation-ledger <JSON> --json` succeeded with authenticated synthetic Evidence and one reviewed obligation each. One input used `shipped-artifact` authority; the other used `controlled-replay`. Each output preserved the corresponding authority in `obligations[0].authority_references[0].authority`, but both returned the same `summary.by_evidence_authority`: `candidate: 0`, `observed: 0`, `reviewed: 1`, `unknown: 0`. The summary groups the obligation's `source_state`. It cannot answer which evidence authority supports even this single-reference obligation. In the shipped-artifact case, the reference state was `candidate`, while the obligation source state and populated summary bucket were both `reviewed`. ### Reproduction The runtime and dependencies were already built and installed from the baseline. The following temporary service test executed two real CLI subprocesses; neither subprocess was mocked. It uses the repository's existing reviewed-obligation fixture and authenticated Evidence constructors. 1. Save this test as `src/application/repro.test.ts` in the baseline checkout: ```ts import { execFile } from "node:child_process"; import { promisify } from "node:util"; import { expect, it } from "vitest"; import { createEvidence } from "../domain/evidence.js"; import { createEvidenceBundle } from "../domain/evidenceBundle.js"; import { reviewedObligation } from "./ReconstructionObligationLedger.fixture.js"; const execute = promisify(execFile); it("compares authority summaries through the public CLI", async () => { const summaries = []; for (const authority of ["shipped-artifact", "controlled-replay"] as const) { const evidence = createEvidence(undefined, { id: "fixture", name: "Fixture", version: "1" }, { operation: "observe", parameters: {}, result: { present: true }, confidence: "observed", authority }); const input = { evidence_bundle: createEvidenceBundle([evidence]), reviewed_obligations: [reviewedObligation("review.one", evidence.evidence_id)], manifest: { bindings: [], contradictions: [] }, }; const { stdout } = await execute(process.execPath, ["scripts/rea.mjs", "build-reconstruction-obligation-ledger", JSON.stringify(input), "--json"], { cwd: process.cwd(), env: process.env, maxBuffer: 16 * 1024 * 1024 }); const ledger = JSON.parse(stdout).normalized_result; expect(ledger.obligations).toHaveLength(1); expect(ledger.obligations[0].authority_references[0].authority).toBe(authority); summaries.push(ledger.summary.by_evidence_authority); console.log(JSON.stringify({ authority, reference_state: ledger.obligations[0].authority_references[0].state, source_state: ledger.obligations[0].source_state, by_evidence_authority: ledger.summary.by_evidence_authority })); } expect(summaries[0]).toEqual(summaries[1]); expect(summaries[0]).toContainEqual({ key: "shipped-artifact", count: 1 }); }, 20_000); ``` 2. Run the focused source test with an isolated home and XDG configuration. Set `REPRO_DIR` to a disposable directory before running: ```sh mkdir -p "$REPRO_DIR/home" "$REPRO_DIR/config" "$REPRO_DIR/cache" "$REPRO_DIR/data" HOME="$REPRO_DIR/home" XDG_CONFIG_HOME="$REPRO_DIR/config" \ XDG_CACHE_HOME="$REPRO_DIR/cache" XDG_DATA_HOME="$REPRO_DIR/data" \ npm run test:local -- src/application/repro.test.ts ``` 3. Both CLI calls return successfully, the authority-preservation assertions pass, and the summary-equality assertion passes. The final assertion fails because there is no `shipped-artifact` bucket. Delete the temporary test afterward. This reproduced in one executed test containing both authority cases. No real provider or target is needed. ### Expected behavior An evidence-authority summary should distinguish the authority values carried by its obligations, rather than label a source-state histogram as an authority breakdown. For a single obligation with one shipped-artifact reference, an authority grouping should account for `shipped-artifact`; the corresponding controlled-replay input should account for `controlled-replay`. At the baseline, the [obligation reference schema](https://github.com/morluto/rea/blob/a5f982de8dbe4b092cc20ab89d492b7e7028b758/src/domain/reconstructionObligationLedgerSchemas.ts#L107-L118) explicitly separates `authority` from `state`. The [coverage guide](https://github.com/morluto/rea/blob/a5f982de8dbe4b092cc20ab89d492b7e7028b758/docs/reconstruction-coverage.md#L14-L24) likewise distinguishes Evidence source authority from confidence. The originating [ledger feature request](https://github.com/morluto/rea/issues/405) specifies a summary grouped by application layer and evidence authority. If a source-state histogram is also intended, its name should identify that dimension. The desired counting policy for obligations with multiple authority references is not established by this reproduction; these inputs each have exactly one reference. ### Impact and scope P2: the authority summary for a supported ledger request returns a different dimension, preventing consumers from obtaining the promised authority breakdown from that summary. A practical workaround is available: read and aggregate `obligations[].authority_references[].authority`; the reproduction confirmed those values remain intact inline. The selector and schema make this a general grouping mismatch, although runtime verification covered only the two single-obligation CLI cases above. No incorrect readiness decision, lost Evidence, native-provider failure, or security boundary crossing was demonstrated. MCP execution and client views were not tested. ### REA version rea-agents 6.7.0; source baseline a5f982de8dbe4b092cc20ab89d492b7e7028b758 ### Runtime and platform Node.js v26.12.0; macOS 27.0.1; Darwin arm64; built CLI with preinstalled locked dependencies; no external provider. ### Target and artifact context Synthetic authenticated Evidence bundle with one caller-reviewed obligation, one authority reference, and an empty reconstruction manifest; no native target. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence Trimmed stdout from the executed test, which parses the actual CLI responses: ```json {"authority":"shipped-artifact","reference_state":"candidate","source_state":"reviewed","by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]} {"authority":"controlled-replay","reference_state":"reviewed","source_state":"reviewed","by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]} ``` The focused test then reported: ```text AssertionError: expected [ …(4) ] to deep equally contain { key: 'shipped-artifact', count: 1 } Test Files 1 failed (1) Tests 1 failed (1) ``` Source checks explain this output: the [summary selector](https://github.com/morluto/rea/blob/a5f982de8dbe4b092cc20ab89d492b7e7028b758/src/domain/reconstructionObligationLedgerIntegrity.ts#L50-L54) reads `source_state`; the [summary schema](https://github.com/morluto/rea/blob/a5f982de8dbe4b092cc20ab89d492b7e7028b758/src/domain/reconstructionObligationLedgerSchemas.ts#L309-L318) restricts bucket keys to the four states. For reviewed input, [candidate derivation](https://github.com/morluto/rea/blob/a5f982de8dbe4b092cc20ab89d492b7e7028b758/src/application/ReconstructionObligationCandidates.ts#L318-L345) preserves each record's authority but fixes the obligation source state to `reviewed`. ### Typed outcome or recovery expected _No response_ ### Analysis or suspected cause Source-derived: the selector and output schema consistently implement a source-state histogram under an authority-summary name. Both need to remain consistent with any corrected contract. The [closure digest includes the summary](https://github.com/morluto/rea/blob/a5f982de8dbe4b092cc20ab89d492b7e7028b758/src/domain/reconstructionObligationLedgerIntegrity.ts#L176-L202), so a corrected summary will also change the resulting digest; the existing digest does not expose the semantic mismatch. ### Additional context _No response_
Author
Owner

@XinwangTao commented on GitHub (Oct 21, 2026):

Confirmed — the summary selector is grouping on the wrong dimension: it buckets by source_state while exposing the result as by_evidence_authority, so the shipped-artifact case (reference candidate, source reviewed) lands in "reviewed" and the promised authority breakdown is unanswerable. Grouping by authority_references[].authority (or renaming the histogram if a state breakdown is intended) plus documenting the multi-reference counting policy would fix the mismatch with the schema and feature request.

<!-- gh-comment-id:6312013527 --> @XinwangTao commented on GitHub (Oct 21, 2026): Confirmed — the summary selector is grouping on the wrong dimension: it buckets by source_state while exposing the result as by_evidence_authority, so the shipped-artifact case (reference candidate, source reviewed) lands in "reviewed" and the promised authority breakdown is unanswerable. Grouping by authority_references[].authority (or renaming the histogram if a state breakdown is intended) plus documenting the multi-reference counting policy would fix the mismatch with the schema and feature request.
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

Confirmed fixed in 25e1a24 via #5875: the summary now groups by cited Evidence authority instead of source_state, and the selector/schema stay consistent with the corrected contract. The digest change is expected since the closure digest includes the summary. The inline aggregation workaround from the report is no longer needed. Thanks for closing this out.

<!-- gh-comment-id:6330280559 --> @XinwangTao commented on GitHub (Oct 22, 2026): Confirmed fixed in 25e1a24 via #5875: the summary now groups by cited Evidence authority instead of source_state, and the selector/schema stay consistent with the corrected contract. The digest change is expected since the closure digest includes the summary. The inline aggregation workaround from the report is no longer needed. Thanks for closing this out.
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#3535
No description provided.