Files
rea/src/application/ReconstructionObligationLedgerService.ts
T
Kaoru0822andClaude Opus 5.5 289aef6838 fix(workflows): keep caller-input details in workflow input errors (#1089)
* fix(managed): keep caller-input details in managed workflow errors

The managed workflows wrapped every rejected input as
`new AnalysisInputError(op, { cause })` with no issues. The projected
result said "Correct the listed arguments and retry" and listed nothing:

- `rea import-managed-reconstruction`,
  `rea project-managed-application-graph` and
  `rea verify-managed-native-boundaries` lost every schema issue for
  malformed JSON;
- domain checks such as a reconstruction text hash mismatch, or Evidence
  from the wrong operation, lost their message on CLI and MCP;
- `rea compare-managed-members` with a missing file did not say which
  path failed.

Add managedInputError, which projects Zod issues with their paths and
keeps a domain TypeError's message, as the JavaScript workflows already
do. Return the BinaryTargetError from an unopenable comparison path
unchanged, with details.path, as every other path-based command does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): keep nested Evidence schema issues off request paths

A ZodError raised after the request parsed comes from a nested
Evidence result, so its issue paths are relative to that result.
Projecting it against the whole request reported, for example,
artifact.sha256 as a missing request field. Keep request-relative
projection for the raw request's schema failures. Report nested schema
failures as root issues whose message names the nested location.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(managed): type the malformed nested Evidence fixture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(workflows): keep input details in the remaining workflow services

Inventory projection (project_android_application_graph and
project_apple_application_graph), build_reconstruction_obligation_ledger
and evaluate_reconstruction_coverage had the same issue-less
`AnalysisInputError({ cause })` as the managed services. For example,
evaluating coverage for an unknown boundary returned "Correct the listed
arguments" with no issues, dropping "Unknown reconstruction boundary: X"
on CLI and MCP.

Move the helpers to src/application/workflowInputError.ts and use them
for these services: requestInputError for raw request schema failures,
workflowInputError for nested schema and domain constraint failures.
Classification is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:56:19 +08:00

80 lines
3.1 KiB
TypeScript

import { z } from "zod";
import { AnalysisProtocolError } from "../domain/analysisErrorCore.js";
import { type AnalysisError } from "../domain/analysisErrorBase.js";
import { createEvidence, type Evidence } from "../domain/evidence.js";
import { jsonObjectSchema, jsonValueSchema } from "../domain/jsonValue.js";
import {
reconstructionObligationLedgerInputSchema,
reconstructionObligationLedgerSchema,
type ReconstructionObligationLedgerInput,
type ReconstructionObligationLedger,
} from "../domain/reconstructionObligationLedgerSchemas.js";
import { err, ok, type Result } from "../domain/result.js";
import { requestInputError, workflowInputError } from "./workflowInputError.js";
import { JAVASCRIPT_APPLICATION_WORKFLOW_PROVIDER } from "./InvestigationProviders.js";
import { deriveReconstructionObligationCandidates } from "./ReconstructionObligationCandidates.js";
import { evaluateReconstructionObligationLedger } from "./ReconstructionObligationLedgerEvaluation.js";
const OPERATION = "build_reconstruction_obligation_ledger" as const;
/** Parse one portable ledger request for both CLI and MCP adapters. */
export const resolveReconstructionObligationLedgerRequest = (
input: unknown,
): Result<ReconstructionObligationLedgerInput, AnalysisError> => {
const parsed = reconstructionObligationLedgerInputSchema.safeParse(input);
return parsed.success
? ok(parsed.data)
: err(requestInputError(OPERATION, parsed.error, input));
};
/** Build the complete deterministic ledger and wrap it in portable Evidence. */
export const buildReconstructionObligationLedgerEvidenceValidated = (
input: ReconstructionObligationLedgerInput,
): Result<Evidence, AnalysisError> => {
try {
const generated = deriveReconstructionObligationCandidates(
input.evidence_bundle,
input.reviewed_obligations,
);
const ledger = evaluateReconstructionObligationLedger({
candidates: generated.candidates,
bundle: input.evidence_bundle,
manifest: input.manifest,
generationLimitations: generated.limitations,
});
return ok(createLedgerEvidence(input, ledger));
} catch (cause: unknown) {
if (cause instanceof z.ZodError)
return err(workflowInputError(OPERATION, cause));
return err(
new AnalysisProtocolError(
"Reconstruction obligation ledger generation failed",
{ cause },
),
);
}
};
const createLedgerEvidence = (
input: ReconstructionObligationLedgerInput,
ledger: ReconstructionObligationLedger,
): Evidence => {
const completeLedger = reconstructionObligationLedgerSchema.parse(ledger);
return createEvidence(undefined, JAVASCRIPT_APPLICATION_WORKFLOW_PROVIDER, {
predicateType: "rea.reconstruction-obligation-ledger",
operation: OPERATION,
parameters: jsonObjectSchema.parse({
ledger_id: completeLedger.ledger_id,
closure_digest: completeLedger.closure_digest,
}),
result: jsonValueSchema.parse(completeLedger),
rawResult: null,
confidence: "inferred",
authority: "analyst-inference",
environment: null,
limitations: completeLedger.limitations,
evidenceLinks: completeLedger.evidence_links,
});
};