[PR #5875] [MERGED] fix(reconstruction)!: summarize obligation ledgers by cited Evidence authority #7097

Closed
opened 2026-11-01 21:45:21 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/5875
Author: @kaoru0822-kitauji
Created: 10/21/2026
Status: ✅ Merged
Merged: 10/21/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: fix/obligation-ledger-authority-summary


📝 Commits (3)

  • 2eda14e fix(reconstruction)!: summarize obligation ledgers by cited Evidence authority
  • f609297 Merge remote-tracking branch 'origin/main' into fix/obligation-ledger-authority-summary
  • 0828f29 Merge remote-tracking branch 'origin/main' into fix/obligation-ledger-authority-summary

📊 Changes

8 files changed (+182 additions, -68 deletions)

View changed files

📝 docs/reconstruction-obligation-ledgers.md (+7 -0)
📝 src/application/ReconstructionObligationLedger.fixture.ts (+60 -0)
📝 src/contracts/reconstructionReadinessExample.ts (+12 -48)
📝 src/domain/evidence.ts (+2 -1)
📝 src/domain/reconstructionObligationLedgerIntegrity.ts (+20 -6)
📝 src/domain/reconstructionObligationLedgerSchemas.ts (+18 -12)
📝 tests/boundary/cli/reconstructionObligationLedgerCli.test.ts (+44 -0)
📝 tests/boundary/mcp/reconstructionObligationLedgerMcp.test.ts (+19 -1)

📄 Description

What broke: summary.by_evidence_authority in the obligation ledger counted each obligation's source_state. A shipped-artifact ledger and a controlled-replay ledger got the same summary.
Fix: The summary now counts obligations by their authority_references[].authority, using the Evidence-owned authority enum. The state histogram moves to a new field, by_source_state.
Risk: Breaking output change. The keys of by_evidence_authority change, by_source_state is now a required field, and ledger closure digests change. No other code in the repo reads the old keys.

Summary

Fixes #5838. Issue #405 asked for a summary "grouped by application layer and evidence authority". The field had the right name, but its keys were the four source states. Clients could not get an authority breakdown from it.

Problem and expected behavior

Smallest trigger: one reviewed obligation that cites a shipped-artifact record, and another that cites a controlled-replay record. Both returned reviewed: 1 under by_evidence_authority. The broken rule: a summary dimension should be derived from the fact it names. Here it was derived from a different field.

Change and scope

  • summarizeReconstructionObligations is the single shared implementation for CLI and MCP. It now counts by_evidence_authority from authority_references[].authority.
  • Counting rule: the field counts obligations, not references, like the other by_* buckets. An obligation counts once under each distinct authority it cites, so the sum can exceed total. The reference state and the obligation source_state do not affect the authority bucket. This rule is documented in the schema .describe() and in docs/reconstruction-obligation-ledgers.md.
  • Unknown or missing authority: this cannot happen. Evidence authority is required, and authority_references must have at least one entry. So there is no unknown authority bucket.
  • Where the authority values come from: evidenceAuthoritySchema is now exported from src/domain/evidence.ts. The authority-reference schema and the summary both use it, replacing a copy of the enum in the ledger schema. The ledger's local state enum now also has one definition.
  • Source-state counts: the old histogram is a real fact, and no other summary field exposed it. It is kept on purpose as by_source_state.
  • Readiness example: reconstructionReadinessExample.ts wrote its summary buckets by hand. It now builds them with the owner function. Its old hand-written values encoded the bug.
  • countBy now makes a single pass and counts distinct keys per item.
  • Not included: how source_state itself is derived (reviewed obligations are always reviewed). That is existing behavior and outside this issue.

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/domain/reconstructionObligationLedgerIntegrity.ts (summary derivation) and reconstructionObligationLedgerSchemas.ts (summary schema and its consistency check).
  • Contribution scope fit and linked maintainer decision: ordinary fix within existing boundaries; not applicable.
  • CLI and MCP/tool-catalog contract: the output schema of build_reconstruction_obligation_ledger changes. by_evidence_authority keys are now the Evidence authority enum, and by_source_state is added as required. The readiness input embeds the ledger, so it gets the same change.
  • Provider, bridge, target-format, or platform compatibility: none.
  • Evidence, artifact, provenance, or reconstruction contract: ledger closure_digest and ledger_id values change for the same inputs, because the digest covers the summary.
  • Target selection, authority, upstream behavior, and cleanup details: not applicable.
  • Generated metadata, package, or installation impact: none committed; metadata:check and docs:check pass.

None of the listed effects apply: the change is a pure derivation over caller-supplied Evidence, with no target interaction.

Evidence and regression coverage

  • Tests added or updated:
    • tests/boundary/cli/reconstructionObligationLedgerCli.test.ts runs the real CLI subprocess.
    • tests/boundary/mcp/reconstructionObligationLedgerMcp.test.ts runs through an in-memory MCP client.
    • Both use the shared mixedAuthorityRequest() fixture: one obligation citing only shipped-artifact, plus one multi-reference obligation citing two shipped-artifact records and one controlled-replay record. Expected: shipped-artifact: 2, controlled-replay: 1, by_source_state.reviewed: 2. The CLI test also checks that shipped-artifact references keep candidate state.
  • Base reproduction: with the domain/contract changes stashed (base sources), both new tests failed. CLI: AssertionError: expected { total: 2, required: 2, …(5) } to match object { total: 2, …(2) }. MCP: the same mismatch. With the fix, both pass.
  • User-visible CLI output: a scratch script outside the repo ran node scripts/rea.mjs build-reconstruction-obligation-ledger <json> --json with authenticated synthetic Evidence.

Before (main f801b2fbf):

{"case":"shipped-artifact","by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]}
{"case":"controlled-replay","by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]}
{"case":"multi-reference","refs":[["shipped-artifact/candidate","controlled-replay/reviewed","shipped-artifact/candidate"],["shipped-artifact/candidate"]],"by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":2},{"key":"unknown","count":0}]}

After:

{"case":"shipped-artifact","by_evidence_authority":[{"key":"shipped-artifact","count":1},{"key":"controlled-replay","count":0},{"key":"historical-reference","count":0},{"key":"external-service","count":0},{"key":"analyst-inference","count":0}],"by_source_state":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]}
{"case":"controlled-replay","by_evidence_authority":[{"key":"shipped-artifact","count":0},{"key":"controlled-replay","count":1},…],"by_source_state":[…,{"key":"reviewed","count":1},…]}
{"case":"multi-reference","by_evidence_authority":[{"key":"shipped-artifact","count":2},{"key":"controlled-replay","count":1},…],"by_source_state":[…,{"key":"reviewed","count":2},…]}
  • Cost: the output grows by two summary buckets, about 190 bytes per ledger. CLI wall time per call was about 360 ms both before and after (single runs, not a benchmark).

  • Remaining proof gaps: not run: npm run verify:readiness, which needs native fixture builds. src/domain/reconstructionReadiness.test.ts, which parses the readiness example, passed. Coverage for generated (Application Graph and process-capture) obligations comes from the shared summarizer and the existing source tests; no new generated-obligation fixture was added.

  • Observed, derived, and inferred claims remain distinguishable.

  • Artifact identity, source provenance, and failed attempts remain preserved.

  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • Reproduction script with the public CLI on main, then on this branch: before and after output as shown above.
  • VITEST_MAX_WORKERS=2 npm run test:focused -- tests/boundary/cli/reconstructionObligationLedgerCli.test.ts tests/boundary/mcp/reconstructionObligationLedgerMcp.test.ts, with base sources: 2 failed, 5 passed (the new tests failed). With the fix: 7 passed.
  • VITEST_MAX_WORKERS=2 npm run test:focused -- on 13 files (ReconstructionObligationCandidates, ReconstructionObligationLedger.authority/.closure, reconstructionObligationLedgerSchemas, reconstructionReadiness, both ledger boundary tests, outputSchemaProfile, outputSchemaSemanticConstraints, toolSchemaValidity, compactInputSchemaMcp, mcpResponseBudget, cliStartupImports): 13 files, 83 tests passed.
  • npm run check:fast: passed.
  • npm run format:check: passed.
  • npm run metadata:check: passed.
  • npm run docs:check: passed.

Compatibility, safety, and release

  • Breaking changes or migration steps:
    • Before, a client read summary.by_evidence_authority with keys candidate | observed | reviewed | unknown. Those counts are now at summary.by_source_state.
    • by_evidence_authority now has the keys shipped-artifact | controlled-replay | historical-reference | external-service | analyst-inference.
    • Keeping the old keys under the authority name would keep the mislabel the issue reports. A strict output schema cannot hold both meanings in one field.
    • Any stored ledger with the old summary no longer validates and needs to be rebuilt.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable (no provider). Tested on macOS arm64, Node v26.
  • Package or release metadata impact: none beyond the breaking-change marker.
  • Security, privacy, process, or containment review: no new side effects; pure derivation over existing input.

Author readiness

Human owner: @kaoru0822-kitauji

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/5875 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/21/2026 **Status:** ✅ Merged **Merged:** 10/21/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `fix/obligation-ledger-authority-summary` --- ### 📝 Commits (3) - [`2eda14e`](https://github.com/morluto/rea/commit/2eda14efb498e8de5fca7f3d3dc785523ce69d0d) fix(reconstruction)!: summarize obligation ledgers by cited Evidence authority - [`f609297`](https://github.com/morluto/rea/commit/f609297c793f8c9578932a47f5f00f69ee5c65ba) Merge remote-tracking branch 'origin/main' into fix/obligation-ledger-authority-summary - [`0828f29`](https://github.com/morluto/rea/commit/0828f29cb3fc4afda7a7d83a965bd8498c1bded2) Merge remote-tracking branch 'origin/main' into fix/obligation-ledger-authority-summary ### 📊 Changes **8 files changed** (+182 additions, -68 deletions) <details> <summary>View changed files</summary> 📝 `docs/reconstruction-obligation-ledgers.md` (+7 -0) 📝 `src/application/ReconstructionObligationLedger.fixture.ts` (+60 -0) 📝 `src/contracts/reconstructionReadinessExample.ts` (+12 -48) 📝 `src/domain/evidence.ts` (+2 -1) 📝 `src/domain/reconstructionObligationLedgerIntegrity.ts` (+20 -6) 📝 `src/domain/reconstructionObligationLedgerSchemas.ts` (+18 -12) 📝 `tests/boundary/cli/reconstructionObligationLedgerCli.test.ts` (+44 -0) 📝 `tests/boundary/mcp/reconstructionObligationLedgerMcp.test.ts` (+19 -1) </details> ### 📄 Description **What broke:** `summary.by_evidence_authority` in the obligation ledger counted each obligation's `source_state`. A shipped-artifact ledger and a controlled-replay ledger got the same summary. **Fix:** The summary now counts obligations by their `authority_references[].authority`, using the Evidence-owned authority enum. The state histogram moves to a new field, `by_source_state`. **Risk:** Breaking output change. The keys of `by_evidence_authority` change, `by_source_state` is now a required field, and ledger closure digests change. No other code in the repo reads the old keys. ## Summary Fixes #5838. Issue #405 asked for a summary "grouped by application layer and evidence authority". The field had the right name, but its keys were the four source states. Clients could not get an authority breakdown from it. ## Problem and expected behavior Smallest trigger: one reviewed obligation that cites a `shipped-artifact` record, and another that cites a `controlled-replay` record. Both returned `reviewed: 1` under `by_evidence_authority`. The broken rule: a summary dimension should be derived from the fact it names. Here it was derived from a different field. ## Change and scope - `summarizeReconstructionObligations` is the single shared implementation for CLI and MCP. It now counts `by_evidence_authority` from `authority_references[].authority`. - **Counting rule:** the field counts obligations, not references, like the other `by_*` buckets. An obligation counts once under each distinct authority it cites, so the sum can exceed `total`. The reference `state` and the obligation `source_state` do not affect the authority bucket. This rule is documented in the schema `.describe()` and in `docs/reconstruction-obligation-ledgers.md`. - **Unknown or missing authority:** this cannot happen. Evidence `authority` is required, and `authority_references` must have at least one entry. So there is no `unknown` authority bucket. - **Where the authority values come from:** `evidenceAuthoritySchema` is now exported from `src/domain/evidence.ts`. The authority-reference schema and the summary both use it, replacing a copy of the enum in the ledger schema. The ledger's local state enum now also has one definition. - **Source-state counts:** the old histogram is a real fact, and no other summary field exposed it. It is kept on purpose as `by_source_state`. - **Readiness example:** `reconstructionReadinessExample.ts` wrote its summary buckets by hand. It now builds them with the owner function. Its old hand-written values encoded the bug. - `countBy` now makes a single pass and counts distinct keys per item. - Not included: how `source_state` itself is derived (reviewed obligations are always `reviewed`). That is existing behavior and outside this issue. ## Contract and boundary impact - Semantic owner and earliest changed stage: `src/domain/reconstructionObligationLedgerIntegrity.ts` (summary derivation) and `reconstructionObligationLedgerSchemas.ts` (summary schema and its consistency check). - Contribution scope fit and linked maintainer decision: ordinary fix within existing boundaries; not applicable. - CLI and MCP/tool-catalog contract: the output schema of `build_reconstruction_obligation_ledger` changes. `by_evidence_authority` keys are now the Evidence authority enum, and `by_source_state` is added as required. The readiness input embeds the ledger, so it gets the same change. - Provider, bridge, target-format, or platform compatibility: none. - Evidence, artifact, provenance, or reconstruction contract: ledger `closure_digest` and `ledger_id` values change for the same inputs, because the digest covers the summary. - Target selection, authority, upstream behavior, and cleanup details: not applicable. - Generated metadata, package, or installation impact: none committed; `metadata:check` and `docs:check` pass. None of the listed effects apply: the change is a pure derivation over caller-supplied Evidence, with no target interaction. ## Evidence and regression coverage - Tests added or updated: - `tests/boundary/cli/reconstructionObligationLedgerCli.test.ts` runs the real CLI subprocess. - `tests/boundary/mcp/reconstructionObligationLedgerMcp.test.ts` runs through an in-memory MCP client. - Both use the shared `mixedAuthorityRequest()` fixture: one obligation citing only shipped-artifact, plus one multi-reference obligation citing two shipped-artifact records and one controlled-replay record. Expected: `shipped-artifact: 2`, `controlled-replay: 1`, `by_source_state.reviewed: 2`. The CLI test also checks that shipped-artifact references keep `candidate` state. - Base reproduction: with the domain/contract changes stashed (base sources), both new tests failed. CLI: `AssertionError: expected { total: 2, required: 2, …(5) } to match object { total: 2, …(2) }`. MCP: the same mismatch. With the fix, both pass. - User-visible CLI output: a scratch script outside the repo ran `node scripts/rea.mjs build-reconstruction-obligation-ledger <json> --json` with authenticated synthetic Evidence. Before (main `f801b2fbf`): ```json {"case":"shipped-artifact","by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]} {"case":"controlled-replay","by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]} {"case":"multi-reference","refs":[["shipped-artifact/candidate","controlled-replay/reviewed","shipped-artifact/candidate"],["shipped-artifact/candidate"]],"by_evidence_authority":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":2},{"key":"unknown","count":0}]} ``` After: ```json {"case":"shipped-artifact","by_evidence_authority":[{"key":"shipped-artifact","count":1},{"key":"controlled-replay","count":0},{"key":"historical-reference","count":0},{"key":"external-service","count":0},{"key":"analyst-inference","count":0}],"by_source_state":[{"key":"candidate","count":0},{"key":"observed","count":0},{"key":"reviewed","count":1},{"key":"unknown","count":0}]} {"case":"controlled-replay","by_evidence_authority":[{"key":"shipped-artifact","count":0},{"key":"controlled-replay","count":1},…],"by_source_state":[…,{"key":"reviewed","count":1},…]} {"case":"multi-reference","by_evidence_authority":[{"key":"shipped-artifact","count":2},{"key":"controlled-replay","count":1},…],"by_source_state":[…,{"key":"reviewed","count":2},…]} ``` - Cost: the output grows by two summary buckets, about 190 bytes per ledger. CLI wall time per call was about 360 ms both before and after (single runs, not a benchmark). - Remaining proof gaps: not run: `npm run verify:readiness`, which needs native fixture builds. `src/domain/reconstructionReadiness.test.ts`, which parses the readiness example, passed. Coverage for generated (Application Graph and process-capture) obligations comes from the shared summarizer and the existing source tests; no new generated-obligation fixture was added. - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - Reproduction script with the public CLI on main, then on this branch: before and after output as shown above. - `VITEST_MAX_WORKERS=2 npm run test:focused -- tests/boundary/cli/reconstructionObligationLedgerCli.test.ts tests/boundary/mcp/reconstructionObligationLedgerMcp.test.ts`, with base sources: 2 failed, 5 passed (the new tests failed). With the fix: 7 passed. - `VITEST_MAX_WORKERS=2 npm run test:focused --` on 13 files (`ReconstructionObligationCandidates`, `ReconstructionObligationLedger.authority`/`.closure`, `reconstructionObligationLedgerSchemas`, `reconstructionReadiness`, both ledger boundary tests, `outputSchemaProfile`, `outputSchemaSemanticConstraints`, `toolSchemaValidity`, `compactInputSchemaMcp`, `mcpResponseBudget`, `cliStartupImports`): 13 files, 83 tests passed. - `npm run check:fast`: passed. - `npm run format:check`: passed. - `npm run metadata:check`: passed. - `npm run docs:check`: passed. ## Compatibility, safety, and release - Breaking changes or migration steps: - Before, a client read `summary.by_evidence_authority` with keys `candidate | observed | reviewed | unknown`. Those counts are now at `summary.by_source_state`. - `by_evidence_authority` now has the keys `shipped-artifact | controlled-replay | historical-reference | external-service | analyst-inference`. - Keeping the old keys under the authority name would keep the mislabel the issue reports. A strict output schema cannot hold both meanings in one field. - Any stored ledger with the old summary no longer validates and needs to be rebuilt. - Real Hopper/Ghidra, browser, or OS coverage: not applicable (no provider). Tested on macOS arm64, Node v26. - Package or release metadata impact: none beyond the breaking-change marker. - Security, privacy, process, or containment review: no new side effects; pure derivation over existing input. ## Author readiness Human owner: @kaoru0822-kitauji - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:45:21 +00:00
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#7097
No description provided.