文档
本页内容
  1. Agent Quick Context
  2. SDK Contract
  3. Executable Assurance

Diagnostic Output Contracts

Tracker references: pm-cha95z, pm-5t33or, pm-f05lsg, pm-h8tpeh, and pm-m4uyyj.

The linked tracker records are repository-only evidence and are excluded from npm.

Agent Quick Context

Failures are context-management surfaces. Every diagnostic family now declares a format-aware token ceiling and one degradation ladder in the public SDK:

Class Text JSON Corrective action that survives degradation
error 768 2000 required action, retry/domain, or next step
warning 768 2000 required action, retry/domain, or next step
validation_summary 1500 3000 required action, retry/domain, or next step
recovery_bundle 768 2000 required action, retry/domain, or next step

The smallest explicit ceiling is 192 estimated tokens. At that floor, the projector may reduce the diagnostic to its code, required action, compact recovery, and exit status. It never removes the first corrective action.

SDK Contract

Use the public contract surface rather than maintaining a package-local error budget:

import {
  PM_DIAGNOSTIC_OUTPUT_BUDGET_CONTRACTS,
  projectPmDiagnosticOutput,
  projectPmDiagnosticText,
  resolvePmDiagnosticOutputBudget,
} from "@unbrained/pm-cli/sdk/contracts";

const contract = resolvePmDiagnosticOutputBudget("error");
const projected = projectPmDiagnosticOutput(
  {
    code: "invalid_argument_value",
    required: "Use --status open and retry.",
    recovery: { suggested_retry: "pm list --status open" },
    detail: "The supplied status is not declared.",
    exit_code: 2,
  },
  { maxEstimatedTokens: contract.minimum_max_estimated_tokens },
);

const text = projectPmDiagnosticText(
  "A long rendered diagnostic",
  "Use --status open and retry.",
).output;

The JSON projector orders code, required, recovery, and next steps before explanation. Untruncated diagnostics add no per-call receipt overhead; their binding declaration is discoverable from pm contracts --full --json under diagnostic_output_contracts. When degradation occurs, the returned diagnostic_output receipt records the effective budget, original and emitted estimates, applied stages, and omitted top-level fields. Minimal fallbacks bound the named omission list and disclose any additional count through omitted_fields_overflow_count.

Structured CLI refusals also include a compact refusal identity with the failing command/flag/operand surface, rejected scalar when observable, complete legal domain when one exists, and the process exit code. Recovery-bearing errors use the recovery_bundle diagnostic class. Collection degradation may compact explanatory domains inside recovery. When refusal.legal_domain is retained, it remains complete. If that object cannot fit the fixed JSON budget, the projector may omit the entire refusal object; the diagnostic_output receipt reports degradation with the bounded omission metadata described above. An absent refusal therefore does not prove that the producer supplied no legal domain. The original SDK error context retains the producer's complete domain; CLI JSON has its own ceiling and is not an unlimited restoration channel. For malformed producer domains, text prioritizes the incomplete-domain notice and a bounded preview of whole usable choices even when producer next steps, verification errors, or recovery coordinates are oversized. An oversized close retry still points to the original SDK recovery metadata. recovery.suggested_retry_args is atomic: the projector retains the complete argv or omits it rather than shortening it into a different command. Tracked by pm-gh1693 (repository-only tracker record, excluded from npm).

Enum, date, get-depth, and minimum intent-budget validation producers attach the failed metadata field and supplied value to PmCliError.context; closed domains also attach the complete legal values, including the accepted full depth alias. CLI attribution normalizes camelCase and snake_case fields to an actually supplied canonical flag, so tokenBudget identifies --token-budget. Explicit flag metadata retains its spelling. Argument order and unrelated valid flags cannot become the refusal owner. The error-context value is text, including the textual representation of numeric SDK inputs. Compound projection conflicts identify their flag without inventing a rejected scalar. Missing items identify the id operand. When the producer cannot identify a field or value, the diagnostic retains the command surface and omits the unknown scalar. Policy evidence takes precedence over syntactic attribution. Tracked by pm-gh1365.

--json and --output-format json (including --output-format=json) select JSON on stderr for parser and execution errors as well as successful output. The canonical --output-format selector wins when combined with --json, so --output-format toon --json retains text diagnostics. Exit codes are unchanged. The executable's project-runtime compatibility warnings and refusals use that same SDK selector, including repeated formats and the -- terminator. A JSON stale-read warning is emitted after successful dispatch; a failed read emits its typed error alone so stderr remains one JSON value. Text warnings retain their pre-dispatch presentation. Tracked by pm-gh1366.

The deterministic ladder is:

  1. full diagnostic;
  2. omit explanation;
  3. limit diagnostic collections;
  4. compact recovery to actionable keys;
  5. retain the action-only envelope.

Human diagnostics lead with What is required and next steps before explaining what happened. If their declared ceiling binds, the compact text still begins with the required action, retains the first identifying line (up to 320 UTF-8 bytes, including an omission suffix), and points to structured JSON for the bounded recovery envelope. The first line should name the specific failure, such as Error: Unknown option --label. Oversized caller-input echoes are discarded before this identity; the retained identity and corrective action share the binding byte budget. The CLI recovery renderer separately retains up to 320 Unicode code points of its What happened text, followed by an omission suffix when needed, before the shared byte-budget projector runs. With malformed legal-domain metadata, the final CLI fallback preserves the producer's required action alongside the partial-domain notice whenever they fit, dropping repeated detail first. The text projector strips terminal escape sequences and replaces remaining control characters before measuring output, including short diagnostics that fit the budget. Line breaks and ordinary indentation are preserved.

Executable Assurance

pnpm quality:recovery-closure builds the current CLI and replays 117 refusal contracts in isolated trackers: 18 closed-domain rows, 88 required-argument omissions derived from core executable positional signatures, seven closed-action families, and four tracker-preflight states. Package-owned commands enter executable coverage when their package runtime is active rather than being misreported as core. Ten representative, high-frequency failure paths are also ratcheted by scripts/release/diagnostic-output-baseline.json. The gate requires every row to remain within the SDK-declared JSON ceiling and retain a mechanically actionable correction. It reports the aggregate original and emitted token estimates without claiming a reduction when no degradation was required.

The baseline is a coverage ratchet, not permission to weaken a ceiling. Its negative control requires a missing baseline probe to fail. The existing refusal-closure negative controls independently prove that incomplete domains, broken retries, and malformed recovery envelopes remain blocking findings. The grammar corpus additionally hashes authoritative tracker state around each refusal; schema, items, history, settings, and package state must not change. Ephemeral runtime lock/cache directories are excluded from that semantic snapshot.

The complete error-code census currently retains executable evidence for 18 catalog rows across 17 canonical groups. Two of those rows are author-manifest schema findings reached through a real pm health --full --strict-exit --json process: an unknown top-level key and omitted canonical pm version bounds. The remaining rows stay explicit uncovered obligations in the generated census (current main, repository-only gate evidence excluded from npm; for an installed release, open the same path at its v<version> tag); the ratchet does not turn partial catalog closure into an approval claim.

Run the focused proof with:

pnpm build
node scripts/release/refusal-closure-gate.mjs
node scripts/run-tests.mjs test -- \
  tests/unit/sdk/agent-output-contracts.spec.ts \
  tests/unit/cli/error-guidance.spec.ts \
  tests/unit/scripts/refusal-closure-gate.spec.ts

来自 pm 2026.11.13 中的 docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md。 在 GitHub 上查看