archctx-contracts 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/fixtures/invalid/hook-debt-checkpoint-failed-read.json +54 -0
  2. package/fixtures/invalid/native-context-invalid-model-summary.json +635 -0
  3. package/fixtures/invalid/native-context-missing-contract-bytes.json +636 -0
  4. package/fixtures/invalid/native-context-missing-input-bytes.json +636 -0
  5. package/fixtures/invalid/native-context-mixed-snapshot.json +637 -0
  6. package/fixtures/invalid/projection-result-applied-incomplete-coverage.json +78 -0
  7. package/fixtures/invalid/refactor-decision-open-status.json +38 -0
  8. package/fixtures/valid/archctx-capabilities.json +3 -3
  9. package/fixtures/valid/capability-dependencies.json +1 -1
  10. package/fixtures/valid/docs-anchors.json +2 -1
  11. package/fixtures/valid/flow-selector-check.json +1 -1
  12. package/fixtures/valid/hook-debt-checkpoint.json +54 -0
  13. package/fixtures/valid/native-context-export.json +636 -0
  14. package/fixtures/valid/product-version-manifest.json +8 -8
  15. package/fixtures/valid/projection-manifest.json +47 -169
  16. package/fixtures/valid/projection-result.json +5 -5
  17. package/fixtures/valid/refactor-decision.json +38 -0
  18. package/fixtures/valid/runtime-recommendation-show.json +1 -0
  19. package/fixtures/valid/runtime-refactor-scan.json +6 -0
  20. package/package.json +1 -1
  21. package/schemas/repo/refactor-decision.schema.json +218 -0
  22. package/schemas/runtime/archctx-capabilities.schema.json +2 -2
  23. package/schemas/runtime/architecture-debt.schema.json +222 -0
  24. package/schemas/runtime/capability-dependencies.schema.json +10 -6
  25. package/schemas/runtime/changeset.schema.json +24 -1
  26. package/schemas/runtime/docs-anchors.schema.json +4 -3
  27. package/schemas/runtime/hook-debt-checkpoint.schema.json +69 -0
  28. package/schemas/runtime/native-context-export.schema.json +823 -0
  29. package/schemas/runtime/practice-checkpoint.schema.json +1 -0
  30. package/schemas/runtime/product-version-manifest.schema.json +2 -2
  31. package/schemas/runtime/projection-apply-readback.schema.json +470 -7
  32. package/schemas/runtime/projection-apply-recovery.schema.json +2 -2
  33. package/schemas/runtime/projection-manifest.schema.json +81 -186
  34. package/schemas/runtime/projection-result.schema.json +132 -5
  35. package/schemas/runtime/projection-target.schema.json +4 -1
  36. package/schemas/runtime/recommendation-v3.schema.json +106 -0
  37. package/schemas/runtime/review-result.schema.json +1 -0
  38. package/schemas/runtime/runtime-recommendation-show.schema.json +234 -13
  39. package/schemas/runtime/runtime-refactor-scan.schema.json +171 -0
  40. package/schemas/runtime/structural-observation-payload.schema.json +106 -0
  41. package/src/architecture-debt.ts +115 -0
  42. package/src/changeset-recovery.ts +49 -0
  43. package/src/docs-anchors.ts +2 -1
  44. package/src/failure.ts +776 -0
  45. package/src/index.ts +6 -0
  46. package/src/ledger.ts +14 -3
  47. package/src/native-context.ts +277 -0
  48. package/src/ownership-dependencies.ts +39 -1
  49. package/src/practices.ts +2 -0
  50. package/src/product-version.ts +37 -4
  51. package/src/projection-manifest.ts +372 -130
  52. package/src/projection-recovery.ts +111 -0
  53. package/src/projection.ts +443 -11
  54. package/src/refactor.ts +525 -2
  55. package/src/schema.ts +6 -0
  56. package/src/validator.ts +8 -3
package/src/failure.ts ADDED
@@ -0,0 +1,776 @@
1
+ import { changeSetRecoveryDetailsSnapshot } from "./changeset-recovery";
2
+ import { PROJECTION_RECOVERY_DETAILS_LIMIT_BYTES, projectionRecoveryDetailsSnapshot } from "./projection-recovery";
3
+ import { ERROR_CATALOG, canonicalize, type ArchContextErrorCode, type Json, type Severity } from "./schema";
4
+
5
+ /** Only selected entrypoints support transport failures; the legacy AC-only envelope is unchanged. */
6
+ export type FailureCode = ArchContextErrorCode | "RPC_TIMEOUT" | "RPC_ABORTED";
7
+ export const FAILURE_STAGES = Object.freeze(["debt", "complete", "practice-checkpoint", "cli", "mcp", "hook-checkpoint", "failure-metadata", "public-operation", "native-export", "docs-anchors", "flows-check"] as const);
8
+ export type FailureStage = typeof FAILURE_STAGES[number];
9
+ export interface StrictFailure<C extends FailureCode = FailureCode> {
10
+ code: C;
11
+ message: string;
12
+ severity: Severity;
13
+ retryable: boolean;
14
+ action: string;
15
+ reasonCode: string;
16
+ details: { [key: string]: Json };
17
+ }
18
+ export interface FailureEnvelope<C extends FailureCode = FailureCode> {
19
+ schemaVersion: "archcontext.envelope/v1";
20
+ ok: false;
21
+ requestId: string;
22
+ data?: never;
23
+ error: StrictFailure<C>;
24
+ }
25
+ type Policy = { code: FailureCode; message: string; fields?: readonly string[]; action?: string; facts?: Readonly<Record<string, string>>; native?: boolean; recovery?: boolean; journalRecovery?: boolean; retryable?: boolean };
26
+ /** Each reason has a closed fact vocabulary. No source text or recovery binding is a summary fact. */
27
+ const policies: Record<string, Policy> = {
28
+ "git-worktree-required": { code: "AC_REPO_NOT_FOUND", message: "This operation requires a Git worktree.", fields: ["checks"] },
29
+ "model-missing": { code: "AC_MODEL_ADOPTION_REQUIRED", message: "Architecture debt requires an adopted model.", fields: ["checks"] },
30
+ "changed-path-invalid": { code: "AC_SCHEMA_INVALID", message: "Changed paths must be an array of repository-relative POSIX paths.", fields: ["checks"] },
31
+ "debt-report-invalid": { code: "AC_SCHEMA_INVALID", message: "Architecture debt evidence violates its contract.", fields: ["checks"] },
32
+ "projection-manifest-upgrade-required": { code: "AC_PRECONDITION_FAILED", message: "The committed projection manifest requires supported re-baselining." },
33
+ "projection-manifest-invalid": { code: "AC_PRECONDITION_FAILED", message: "The committed projection manifest violates its contract." },
34
+ "projection-footprint-uncovered": { code: "AC_PRECONDITION_FAILED", message: "Declared source footprints lack a projection target." },
35
+ "index-missing": { code: "AC_CODE_FACTS_UNAVAILABLE", message: "Required code facts have no available index. Run codegraph init." },
36
+ "checkpoint-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Checkpoint arguments violate the declared input schema.", fields: ["issueCount"], action: "correct-command-arguments" },
37
+ "cli-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Command arguments violate the declared input contract.", action: "correct-command-arguments" },
38
+ "complete-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Completion input violates the caller authority contract.", action: "correct-command-arguments" },
39
+ "hook-event-invalid": { code: "AC_SCHEMA_INVALID", message: "Hook event violates the declared input contract.", action: "correct-command-arguments" },
40
+ "egress-policy-denied": { code: "AC_POLICY_VIOLATION", message: "Local policy refused this operation." },
41
+ "runtime-version-unsupported": { code: "AC_RUNTIME_VERSION_UNSUPPORTED", message: "The connected runtime version is unsupported." },
42
+ "rpc-timeout": { code: "RPC_TIMEOUT", message: "The runtime RPC deadline elapsed without a response; a mutation outcome is unknown.", fields: ["method", "timeoutMs", "elapsedMs", "mutationOutcome"], action: "reconcile-before-retry" },
43
+ "rpc-aborted": { code: "RPC_ABORTED", message: "The runtime RPC caller aborted without a response; a mutation outcome is unknown.", fields: ["method", "timeoutMs", "elapsedMs", "mutationOutcome"], action: "reconcile-before-retry" },
44
+ "failure-unclassified": { code: "AC_UNCLASSIFIED_FAILURE", message: "The operation failed without a classified cause. Inspect local diagnostics before retrying.", action: "inspect-diagnostics" },
45
+ };
46
+ // C1 policies are selected at explicit producer branches, never from diagnostic text.
47
+ Object.assign(policies, {
48
+ "developer-review-run-not-owned": {code:"AC_PRECONDITION_FAILED",message:"developer-review-run-not-owned",facts:{refusal:"reviewOwnershipRefusal"}},
49
+ "developer-review-attestation-input-invalid": {code:"AC_SCHEMA_INVALID",message:"Caller-provided attestation fields are forbidden.",facts:{issueCount:"uint"}},
50
+ "init-would-overwrite-existing-model": {"code": "AC_PRECONDITION_FAILED", "message": "init-would-overwrite-existing-model: an existing architecture model must not be overwritten.", "facts": {}},
51
+ "waiver-list-unreadable": {"code": "AC_SCHEMA_INVALID", "message": "Practice waiver list or owner registry could not be read.", "facts": {}},
52
+ "developer-review-run-already-active": {"code": "AC_PRECONDITION_FAILED", "message": "developer-review-run-already-active", "facts": {"challengeId": "id"}},
53
+ "developer-review-run-prepare-failed": {"code": "AC_PRECONDITION_FAILED", "message": "developer-review-run-prepare-failed", "facts": {}},
54
+ "developer-review-worktree-invalid": {"code": "AC_PRECONDITION_FAILED", "message": "developer-review-worktree-invalid", "facts": {"refusal":"reviewWorktreeRefusal"}},
55
+ "device-private-key-signer-unavailable": {"code": "AC_PRECONDITION_FAILED", "message": "device-private-key-signer-unavailable", "facts": {}},
56
+
57
+ "ledger-worktree-claim-required": {code:"AC_PRECONDITION_FAILED", message:"Ledger write requires expectedWorktreeDigest.", facts:{}},
58
+ "ledger-worktree-claim-changed": {code:"AC_PRECONDITION_FAILED", message:"Worktree digest changed before ledger write.", facts:{expectedWorktreeDigest:"sha256",actualWorktreeDigest:"sha256"}},
59
+ "rpc-version-mismatch": {code:"AC_RUNTIME_VERSION_UNSUPPORTED", message:"Runtime RPC version mismatch; run archctx daemon upgrade.",action:"upgrade-archctx-runtime",facts:{expected:"rpcVersion",received:"rpcVersion",action:"upgradeAction",upgradeCommand:"upgradeCommand"}},
60
+ "semantic-model-directory-invalid": {"code": "AC_PRECONDITION_FAILED", "message": "semantic-model-directory-invalid.", "facts": {}},
61
+ "semantic-model-entry-not-regular": {"code": "AC_PRECONDITION_FAILED", "message": "semantic-model-entry-not-regular.", "facts": {}},
62
+ "accepted-committed-change-not-utf8": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-not-utf8.", "facts": {}},
63
+ "accepted-committed-change-journal-lookup-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-journal-lookup-mismatch", "facts": {}},
64
+ "accepted-committed-change-journal-not-committed": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-journal-not-committed.", "facts": {}},
65
+ "accepted-committed-change-journal-id-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-journal-id-mismatch.", "facts": {}},
66
+ "accepted-committed-change-journal-without-semantic-write": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-journal-without-semantic-write.", "facts": {}},
67
+ "accepted-committed-change-journal-without-transition": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-journal-without-transition.", "facts": {}},
68
+ "accepted-committed-change-journal-out-of-order": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-journal-out-of-order.", "facts": {}},
69
+ "accepted-committed-change-chain-empty": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-chain-empty", "facts": {}},
70
+ "accepted-committed-change-chain-baseline-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-chain-baseline-mismatch.", "facts": {}},
71
+ "accepted-committed-change-chain-gap": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-chain-gap.", "facts": {"previousJournalId":"id","journalId":"id"}},
72
+ "accepted-committed-change-chain-current-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-chain-current-mismatch.", "facts": {}},
73
+ "accepted-committed-change-node-path-nonstandard": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-node-path-nonstandard.", "facts": {}},
74
+ "accepted-committed-change-deleted-node-unbound": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-deleted-node-unbound.", "facts": {}},
75
+ "accepted-committed-change-proof-change-unexplained": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-proof-change-unexplained.", "facts": {"capabilityId":"id"}},
76
+ "accepted-committed-change-model-snapshot-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-model-snapshot-mismatch: the model changed during acceptance", "facts": {}},
77
+ "accepted-committed-change-projection-model-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-projection-model-mismatch", "facts": {}},
78
+ "accepted-committed-change-projection-rejected": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-projection-rejected: resolve adoption or ownership conflicts first", "facts": {}},
79
+ "accepted-committed-change-proof-unprovable": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-proof-unprovable.", "facts": {}},
80
+ "accepted-committed-change-no-unresolved-major-change": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-no-unresolved-major-change.", "facts": {}},
81
+ "accepted-committed-change-observed-delta-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-observed-delta-mismatch", "facts": {}},
82
+ "accepted-committed-change-reference-not-resolvable": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-reference-not-resolvable", "facts": {}},
83
+ "accepted-committed-change-head-unreadable": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-head-unreadable", "facts": {}},
84
+ "accepted-committed-change-baseline-missing": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-baseline-missing: no projection manifest", "facts": {}},
85
+ "accepted-committed-change-baseline-changed": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-baseline-changed: projection manifest moved during acceptance", "facts": {}},
86
+ "accepted-committed-change-baseline-unanchored": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-baseline-unanchored.", "facts": {}},
87
+ "accepted-committed-change-file-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-file-mismatch.", "facts": {}},
88
+ "accepted-committed-change-node-id-missing": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-node-id-missing.", "facts": {}},
89
+ "accepted-committed-change-path-unprovable": {"code": "AC_PRECONDITION_FAILED", "message": "accepted-committed-change-path-unprovable: symlink or semantic path authority cannot be proven.", "facts": {}},
90
+ "projection-inputs-changed-during-run": {"code": "AC_PRECONDITION_FAILED", "message": "architecture-docs-projection-inputs-changed-during-run", "facts": {"field": "projectionInputField"}},
91
+
92
+ "daemon-stop-identity-mismatch": {code:"AC_RUNTIME_VERSION_UNSUPPORTED",message:"Expected daemon identity does not match this live instance; no shutdown performed.",facts:{}},
93
+ "projection-apply-first-delivery-unavailable": {"code": "AC_PRECONDITION_FAILED", "message": "Projection apply first delivery unavailable.", "facts": {"lookupKey": "sha256", "applyId": "sha256"}},
94
+ "projection-apply-readback-proof-unavailable": {"code": "AC_PRECONDITION_FAILED", "message": "Projection apply readback proof unavailable.", "facts": {"lookupKey": "sha256", "applyId": "sha256"}},
95
+ "projection-apply-restored-proof-unavailable": {"code": "AC_PRECONDITION_FAILED", "message": "Projection apply restored proof unavailable.", "facts": {"lookupKey": "sha256", "applyId": "sha256"}},
96
+ "projection-recovery-delivery-unavailable": {"code": "AC_PRECONDITION_FAILED", "message": "Projection recovery delivery unavailable.", "facts": {"lookupKey": "sha256", "applyId": "sha256"}},
97
+ "projection-recovery-receipt-missing": {"code": "AC_PRECONDITION_FAILED", "message": "Projection recovery receipt missing.", "facts": {"lookupKey": "sha256", "applyId": "sha256"}},
98
+ "projection-recovery-proof-missing": {"code": "AC_PRECONDITION_FAILED", "message": "Projection recovery proof missing.", "facts": {"lookupKey": "sha256", "applyId": "sha256"}},
99
+ "projection-recovery-authority-changed": {"code": "AC_PRECONDITION_FAILED", "message": "Projection recovery authority changed.", "facts": {"lookupKey": "sha256", "applyId": "sha256"}},
100
+ "projection-apply-restore-unproven": {"code": "AC_PRECONDITION_FAILED", "message": "Projection apply restore unproven.", "facts": {"lookupKey": "sha256", "applyId": "sha256", "issueCount": "uint"}},
101
+ "projection-readback-proof-failed": {"code": "AC_PRECONDITION_FAILED", "message": "Projection readback proof failed.", "facts": {"lookupKey": "sha256", "applyId": "sha256", "issueCount": "uint"}},
102
+ "projection-readback-authority-changed": {"code": "AC_PRECONDITION_FAILED", "message": "Projection readback authority changed.", "facts": {"lookupKey": "sha256", "applyId": "sha256", "issueCount": "uint"}},
103
+ "projection-recovery-proof-failed": {"code": "AC_PRECONDITION_FAILED", "message": "Projection recovery proof failed.", "facts": {"lookupKey": "sha256", "applyId": "sha256", "issueCount": "uint"}},
104
+ "projection-ownership-repair-required": {"code": "AC_PRECONDITION_FAILED", "message": "Projection ownership repair required.", "facts": {"issueCount": "uint"}},
105
+ "projection-orphan-review-required": {"code": "AC_PRECONDITION_FAILED", "message": "Projection orphan review required.", "facts": {"issueCount": "uint"}},
106
+ "projection-adoption-refused": {"code": "AC_PRECONDITION_FAILED", "message": "Projection adoption refused.", "facts": {"issueCount": "uint"}},
107
+ "projection-readback-absence-invalid": {"code": "AC_PRECONDITION_FAILED", "message": "Projection readback absence invalid.", "facts": {"issueCount": "uint"}},
108
+ "projection-adoption-preimage-missing": {"code": "AC_PRECONDITION_FAILED", "message": "Projection adoption preimage missing.", "facts": {}},
109
+ "projection-adoption-preview-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "Projection adoption preview mismatch.", "facts": {}},
110
+ "projection-accepted-fixed-point-unproven": {"code": "AC_PRECONDITION_FAILED", "message": "Projection accepted fixed point unproven.", "facts": {}},
111
+ "projection-adoption-fixed-point-unproven": {"code": "AC_PRECONDITION_FAILED", "message": "Projection adoption fixed point unproven.", "facts": {"driftCount": "uint", "rejectedCount": "uint", "digestStable": "boolean"}},
112
+ "projection-adoption-profile-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Projection adoption profile invalid.", "facts": {}},
113
+ "projection-adoption-approval-input-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Projection adoption approval input invalid.", "facts": {}},
114
+ "agent-context-input-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Agent context input invalid.", "facts": {}},
115
+ "projection-readback-input-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Projection readback input invalid.", "facts": {}},
116
+ "projection-recovery-input-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Projection recovery input invalid.", "facts": {}},
117
+ "projection-readback-result-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Projection readback result invalid.", "facts": {"issueCount": "uint"}},
118
+ "projection-recovery-result-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Projection recovery result invalid.", "facts": {"issueCount": "uint"}},
119
+ "projection-prior-applies-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Projection prior applies invalid.", "facts": {"issueCount": "uint"}},
120
+ "projection-ambiguous-ownership": {"code": "AC_PRECONDITION_FAILED", "message": "Projection ownership is ambiguous. Review the listed paths; remove unvouched whole-file outputs from Git and reapply explicitly. Use docs drift to inspect the complete output list when diagnostics are omitted.", "facts": {"pathCount": "uint", "paths": "optional:diagnosticPaths", "action": "ownershipAction", "commands": "optional:diagnosticCommands", "omittedPathCount": "uint", "omittedCommandCount": "uint"}},
121
+
122
+ "changeset-recovery-unresolved": {"code": "AC_PRECONDITION_FAILED", "message": "changeset-recovery-unresolved: startup journals require repair; inspect the complete recovery authority and restart archctxd to retry recovery.", "journalRecovery": true},
123
+ "apply-input-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Apply input invalid.", "facts": {}},
124
+ "apply-approval-required": {"code": "AC_USER_CONFIRMATION_REQUIRED", "message": "Review the ChangeSet preview, then apply explicitly with approved: true (CLI: --approved).", "facts": {}},
125
+ "apply-repository-mismatch": {"code": "AC_PRECONDITION_FAILED", "message": "ChangeSet was planned for a different repository.", "facts": {}},
126
+ "apply-draft-missing": {"code": "AC_PRECONDITION_FAILED", "message": "Unknown ChangeSet; create a new plan before applying.", "facts": {"changeSetId": "id"}},
127
+ "apply-profile-missing": {"code": "AC_PRECONDITION_FAILED", "message": "ChangeSet worktree digest profile missing before apply.", "facts": {}},
128
+ "apply-profile-changed": {"code": "AC_PRECONDITION_FAILED", "message": "ChangeSet worktree digest profile changed before apply.", "facts": {}},
129
+ "apply-worktree-changed": {"code": "AC_PRECONDITION_FAILED", "message": "Worktree digest changed before apply.", "facts": {"expectedWorktreeDigest": "sha256", "actualWorktreeDigest": "sha256"}},
130
+ "apply-head-changed": {"code": "AC_PRECONDITION_FAILED", "message": "ChangeSet HEAD changed before apply.", "facts": {"expectedHeadSha": "head", "actualHeadSha": "head"}},
131
+ "apply-model-invalid": {"code": "AC_PRECONDITION_FAILED", "message": "ChangeSet base model is invalid.", "facts": {"issueCount": "uint"}},
132
+ "apply-model-changed": {"code": "AC_PRECONDITION_FAILED", "message": "ChangeSet model digest changed before apply.", "facts": {"expectedModelDigest": "sha256", "actualModelDigest": "sha256"}},
133
+ "apply-preimage-changed": {"code": "AC_PRECONDITION_FAILED", "message": "Projection preimage changed before apply.", "facts": {"issueCount": "uint"}},
134
+ "projection-input-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Projection input invalid.", "facts": {}},
135
+ "projection-approval-required": {"code": "AC_USER_CONFIRMATION_REQUIRED", "message": "Review the projection request, then call again with approved: true.", "facts": {}},
136
+ "waiver-owner-registry-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Practice waiver owner registry invalid.", "facts": {}},
137
+ "waiver-input-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Practice waiver input invalid.", "facts": {}},
138
+ "worktree-claim-required": {"code": "AC_REFACTOR_STALE", "message": "This operation requires expectedWorktreeDigest.", "facts": {}},
139
+ "worktree-claim-changed": {"code": "AC_REFACTOR_STALE", "message": "Worktree digest changed before this operation.", "facts": {"expectedWorktreeDigest": "sha256", "actualWorktreeDigest": "sha256"}},
140
+ "accept-committed-worktree-changed": {"code": "AC_PRECONDITION_FAILED", "message": "Accepted ChangeSet expected worktree digest mismatch.", "facts": {"expectedWorktreeDigest": "sha256", "actualWorktreeDigest": "sha256"}},
141
+ "accept-committed-plan-changed": {"code": "AC_PRECONDITION_FAILED", "message": "Accepted ChangeSet acceptance plan changed since preview.", "facts": {"expectedPlanDigest": "sha256", "actualPlanDigest": "sha256"}},
142
+ "accept-committed-readback-failed": {"code": "AC_PRECONDITION_FAILED", "message": "Committed ChangeSet acceptance readback failed.", "facts": {}},
143
+ "accept-committed-event-conflict": {"code": "AC_PRECONDITION_FAILED", "message": "Committed ChangeSet already has a different acceptance event at this snapshot.", "facts": {}},
144
+ "accept-committed-append-failed": {"code": "AC_PRECONDITION_FAILED", "message": "Committed ChangeSet acceptance event was not appended.", "facts": {}},
145
+ "runtime-not-running": {"code": "AC_PRECONDITION_FAILED", "message": "archctxd is not running.", "facts": {}},
146
+ "runtime-writer-locked": {"code": "AC_PRECONDITION_FAILED", "message": "Runtime writer is locked.", "facts": {}},
147
+ "runtime-ledger-mode-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Runtime architecture ledger mode invalid.", "facts": {"field": "ledgerModeField"}},
148
+ "runtime-writer-owned": {"code": "AC_PRECONDITION_FAILED", "message": "local-store-writer-owned: another live process owns this store.", "facts": {}},
149
+ "rpc-input-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Runtime RPC input invalid.", "facts": {}},
150
+ "rpc-method-invalid": {"code": "AC_SCHEMA_INVALID", "message": "Unknown runtime RPC method. Upgrade the client.", "facts": {}, "action":"upgrade-client"},
151
+ "changeset-rollback-conflict": {code: "AC_PRECONDITION_FAILED", message: "ChangeSet rollback is incomplete because the projection manifest changed externally. Preserve the pending journal and backup for manual recovery.", facts: {journalId: "optional:id", changeSetId: "id", manualRecoveryRequired: "boolean"}},
152
+ "deps-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Deps input invalid.","facts": {"field":"optional:depsField","issue":"optional:depsIssue"}},
153
+ "deps-workspace-invalid": {"code": "AC_SCHEMA_INVALID","message": "Deps workspace invalid.","facts": {}},
154
+ "deps-model-unreadable": {"code": "AC_MODEL_ADOPTION_REQUIRED","message": "Deps model unreadable.","facts": {}},
155
+ "code-facts-unavailable": {"code": "AC_CODE_FACTS_UNAVAILABLE","message": "Code facts unavailable.","facts": {}},
156
+ "projection-check-mode-invalid": {"code": "AC_SCHEMA_INVALID","message": "Projection check mode invalid.","facts": {}},
157
+ "projection-check-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Projection check input invalid. Supply valid fields and keep lists sorted and unique.","facts": {}},
158
+ "projection-snapshot-mismatch": {"code": "AC_PRECONDITION_FAILED","message": "Projection snapshot mismatch.","facts": {"field":"optional:snapshotField"}},
159
+ "changed-path-outside-worktree": {"code": "AC_SCHEMA_INVALID","message": "Changed path outside worktree.","facts": {}},
160
+ "changed-path-not-normalized": {"code": "AC_SCHEMA_INVALID","message": "Changed path not normalized.","facts": {}},
161
+ "projection-result-invalid": {"code": "AC_SCHEMA_INVALID","message": "Projection result invalid.","facts": {}},
162
+ "repository-not-registered": {"code": "AC_REPO_NOT_FOUND","message": "Repository not registered.","facts": {}},
163
+ "landscape-invalid": {"code": "AC_SCHEMA_INVALID","message": "Landscape invalid.","facts": {}},
164
+ "landscape-repositories-required": {"code": "AC_PRECONDITION_FAILED","message": "Landscape repositories required.","facts": {},"action": "archctx repo add"},
165
+ "docs-provider-invalid": {"code": "AC_SCHEMA_INVALID","message": "Docs provider invalid.","facts": {}},
166
+ "docs-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Docs input invalid.","facts": {}},
167
+ "docs-network-authorization-required": {"code": "AC_SCHEMA_INVALID","message": "Docs network authorization required. Pass --allow-network to opt in.","facts": {}},
168
+ "docs-pin-required": {"code": "AC_SCHEMA_INVALID","message": "Docs pin required.","facts": {}},
169
+ "docs-command-invalid": {"code": "AC_SCHEMA_INVALID","message": "Docs command invalid.","facts": {}},
170
+ "resource-uri-invalid": {"code": "AC_SCHEMA_INVALID","message": "Resource uri invalid.","facts": {}},
171
+ "resource-cache-miss": {"code": "AC_SCHEMA_INVALID","message": "Resource cache miss.","facts": {}},
172
+ "explorer-query-invalid": {"code": "AC_SCHEMA_INVALID","message": "Explorer query invalid.","facts": {}},
173
+ "explorer-precondition-failed": {"code": "AC_PRECONDITION_FAILED","message": "Explorer precondition failed.","facts": {"inputDomain":"optional:observedDomain","availability":"optional:inputAvailability","cause":"optional:unclassifiedCause"}},
174
+ "invalid-delta-query": {"code": "AC_SCHEMA_INVALID","message": "Invalid delta query.","facts": {}},
175
+ "projection-cache-miss": {"code": "AC_PRECONDITION_FAILED","message": "Projection cache miss.","facts": {}},
176
+ "authority-event-missing": {"code": "AC_PRECONDITION_FAILED","message": "Authority event missing.","facts": {}},
177
+ "authority-cursor-reversed": {"code": "AC_PRECONDITION_FAILED","message": "Authority cursor reversed.","facts": {}},
178
+ "projection-authority-mismatch": {"code": "AC_PRECONDITION_FAILED","message": "Projection authority mismatch.","facts": {}},
179
+ "projection-manifest-incompatible": {"code": "AC_PRECONDITION_FAILED","message": "Projection manifest incompatible.","facts": {}},
180
+ "practice-catalog-invalid": {"code": "AC_SCHEMA_INVALID","message": "Practice catalog invalid.","facts": {}},
181
+ "practice-not-found": {"code": "AC_SCHEMA_INVALID","message": "Practice not found.","facts": {}},
182
+ "practice-command-invalid": {"code": "AC_SCHEMA_INVALID","message": "Practice command invalid.","facts": {}},
183
+ "production-injections-refused": {"code": "AC_SCHEMA_INVALID","message": "Production injections refused.","facts": {"issueCount": "uint"}},
184
+ "plan-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Plan input invalid.","facts": {}},
185
+ "plan-worktree-changed": {"code": "AC_PRECONDITION_FAILED","message": "Plan worktree changed.","facts": {"expectedWorktreeDigest": "sha256","actualWorktreeDigest": "sha256"}},
186
+ "accept-committed-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Accept committed input invalid.","facts": {}},
187
+ "accept-committed-authority-required": {"code": "AC_PRECONDITION_FAILED","message": "Accept committed authority required.","facts": {}},
188
+ "ledger-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Ledger input invalid.","facts": {}},
189
+ "book-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Book input invalid.","facts": {}},
190
+ "recommendations-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Recommendations input invalid.","facts": {}},
191
+ "recommendation-snapshot-changed": {"code": "AC_REFACTOR_STALE","message": "Recommendation snapshot changed.","facts": {"recommendationId": "id","changedFields": "changedFields"}},
192
+ "recommendation-decision-invalid": {"code": "AC_PRECONDITION_FAILED","message": "Recommendation decision invalid.","facts": {}},
193
+ "refactor-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Refactor input invalid.","facts": {}},
194
+ "refactor-assessment-missing": {"code": "AC_SCHEMA_INVALID","message": "Refactor assessment missing.","facts": {"assessmentDigest": "sha256"}},
195
+ "refactor-assessment-stale": {"code": "AC_REFACTOR_STALE","message": "Refactor assessment stale.","facts": {"assessmentDigest": "sha256","expectedHeadSha": "head","actualHeadSha": "head","expectedWorktreeDigest": "sha256","actualWorktreeDigest": "sha256"}},
196
+ "refactor-proposal-unauthored": {"code": "AC_REFACTOR_PROPOSAL_UNAUTHORED","message": "Refactor proposal unauthored.","facts": {"issueCount": "uint"}},
197
+ "refactor-invariant-invalid": {"code": "AC_SCHEMA_INVALID","message": "Refactor invariant invalid.","facts": {"issueCount": "uint"}},
198
+ "refactor-namespace-changed": {"code": "AC_REFACTOR_STALE","message": "Refactor namespace changed.","facts": {"assessmentDigest": "sha256","expectedNamespace": "id","actualNamespace": "id"}},
199
+ "refactor-record-snapshot-changed": {"code": "AC_REFACTOR_STALE","message": "Refactor record snapshot changed.","facts": {"assessmentDigest": "sha256","changedFields": "changedFields"}},
200
+ "refactor-head-mismatch": {"code": "AC_REFACTOR_STALE","message": "Refactor head mismatch.","facts": {"recommendationId": "id","expectedHeadSha": "head","actualHeadSha": "head"}},
201
+ "refactor-recommendation-missing": {"code": "AC_SCHEMA_INVALID","message": "Refactor recommendation missing.","facts": {"recommendationId": "id"}},
202
+ "refactor-verify-snapshot-changed": {"code": "AC_REFACTOR_STALE","message": "Refactor verify snapshot changed.","facts": {"recommendationId": "id","changedFields": "changedFields"}},
203
+ "refactor-decision-approval-required": {"code": "AC_USER_CONFIRMATION_REQUIRED","message": "Refactor decision approval required.","facts": {"recommendationId": "id"}},
204
+ "jobs-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Jobs input invalid.","facts": {"field":"optional:jobsField"}},
205
+ "job-running-required": {"code": "AC_PRECONDITION_FAILED","message": "Job running required.","facts": {"jobId": "id"}},
206
+ "job-context-stale": {"code": "AC_CONTEXT_STALE","message": "Job context stale.","facts": {"jobId": "id","expectedHeadSha": "head","actualHeadSha": "head","expectedWorktreeDigest": "sha256","actualWorktreeDigest": "sha256"}},
207
+ "audit-disabled": {"code": "AC_CAPABILITY_UNSUPPORTED","message": "Audit disabled.","facts": {}},
208
+ "audit-active": {"code": "AC_PRECONDITION_FAILED","message": "Audit active.","facts": {}},
209
+ "audit-claim-failed": {"code": "AC_PRECONDITION_FAILED","message": "Audit claim failed.","facts": {"jobId": "id"}},
210
+ "audit-run-missing": {"code": "AC_REPO_NOT_FOUND","message": "Audit run missing.","facts": {"runId": "id"}},
211
+ "audit-run-failed": {"code": "AC_PRECONDITION_FAILED","message": "Audit run failed.","facts": {"runId": "id"}},
212
+ "audit-already-issuing": {"code": "AC_PRECONDITION_FAILED","message": "Audit already issuing.","facts": {"runId": "id"}},
213
+ "audit-proposal-invalid": {"code": "AC_SCHEMA_INVALID","message": "Audit proposal invalid.","facts": {"runId": "id"}},
214
+ "audit-drafts-required": {"code": "AC_PRECONDITION_FAILED","message": "Audit drafts required.","facts": {"runId": "id"}},
215
+ "audit-confirmation-required": {"code": "AC_USER_CONFIRMATION_REQUIRED","message": "Review the public repository target, then use archctx audit approve with --confirm-public-repo and the complete confirmationToken.","facts": {"runId": "id", "confirmationToken": "publicConfirmationToken"}},
216
+ "audit-preflight-refused": {"code": "AC_PRECONDITION_FAILED","message": "Audit preflight refused.","facts": {"runId": "id"}},
217
+ "audit-github-refused": {"code": "AC_PRECONDITION_FAILED","message": "Audit github refused.","facts": {"runId": "id"}},
218
+ "hook-input-invalid": {"code": "AC_SCHEMA_INVALID","message": "Hook input invalid.","facts": {}},
219
+ "runtime-agent-job-out-of-scope": {"code": "AC_PRECONDITION_FAILED","message": "Runtime agent job out of scope.","facts": {"jobId": "id"}},
220
+ "audit-user-consent-required": {"code": "AC_USER_CONFIRMATION_REQUIRED","message": "Audit user consent required.","facts": {"consentReason": "consentReason"}},
221
+ "recommendation-not-found": {"code": "AC_REFACTOR_STALE","message": "Recommendation not found.","facts": {"recommendationId": "id","searched": "searched"}},
222
+ "git-decision-subject-not-observed": {"code": "AC_REFACTOR_STALE","message": "Git decision subject not observed.","facts": {"recommendationId": "id"}},
223
+ "scan-candidate-not-recorded": {"code": "AC_REFACTOR_STALE","message": "Scan candidate not recorded.","facts": {"recommendationId": "id"}},
224
+ "ledger-audit-generation": {"code": "AC_REFACTOR_STALE","message": "Ledger audit generation.","facts": {"currentRecommendationId": "id","currentRecorded": "boolean"}},
225
+ "git-decision-not-recorded": {"code": "AC_REFACTOR_EVIDENCE_REQUIRED","message": "Git decision not recorded.","facts": {"recommendationId": "id","decisionStatus": "decisionStatus", "recordCommand": "commandSuggestion", "verifyCommand": "commandSuggestion"}},
226
+ "recommendation-v2-not-migrated": {"code": "AC_PRECONDITION_FAILED","message": "Recommendation v2 not migrated.","facts": {"recommendationId": "id"}},
227
+ "evidence-digest-missing": {"code": "AC_REFACTOR_EVIDENCE_REQUIRED","message": "Evidence digest missing.","facts": {"recommendationId": "id"}},
228
+ "evidence-unknown": {"code": "AC_REFACTOR_EVIDENCE_REQUIRED","message": "Evidence unknown.","facts": {"recommendationId": "id","evidenceDigest": "sha256"}},
229
+ "evidence-head-drift": {"code": "AC_REFACTOR_STALE","message": "Evidence head drift.","facts": {"recommendationId": "id","evidenceDigest": "sha256","expectedHeadSha": "head","actualHeadSha": "head", "expectedWorktreeDigest": "optional:sha256", "actualWorktreeDigest": "optional:sha256", "changedFields": "optional:changedFields", "observation": "optional:evidenceObservation"}},
230
+ "evidence-worktree-drift": {"code": "AC_REFACTOR_STALE","message": "Evidence worktree drift.","facts": {"recommendationId": "id","evidenceDigest": "sha256","expectedWorktreeDigest": "sha256","actualWorktreeDigest": "sha256"}},
231
+ "evidence-not-resolved": {"code": "AC_REFACTOR_EVIDENCE_REQUIRED","message": "Evidence not resolved.","facts": {"recommendationId": "id","evidenceDigest": "sha256","disposition": "disposition"}},
232
+ "refactor-run-exceeds-ledger-size-limit": {"code": "AC_REFACTOR_RUN_TOO_LARGE","message": "Refactor run exceeds ledger size limit.","facts": {"measuredBytes": "uint","limitBytes": "uint"}},
233
+ "product-id-missing": {"code": "AC_MODEL_ADOPTION_REQUIRED","message": "Product id missing.","facts": {}},
234
+ "product-id-invalid": {"code": "AC_SCHEMA_INVALID","message": "Product id invalid.","facts": {}},
235
+ "refactor-decision-invalid": {"code": "AC_SCHEMA_INVALID","message": "Refactor decision invalid.","facts": {}},
236
+ "context7-provider-refused": {"code": "AC_SCHEMA_INVALID","message": "Context7 provider refused.","facts": {"kind": "context7Kind"}},
237
+ "native-context-arguments-invalid": {"code": "AC_SCHEMA_INVALID","message": "native-context-arguments-invalid","facts": {"flag": "diagnostic","category": "diagnostic","supportedFormat": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
238
+ "native-context-profile-invalid": {"code": "AC_SCHEMA_INVALID","message": "native-context-profile-invalid","facts": {"supportedProfile": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
239
+ "native-context-model-missing": {"code": "AC_MODEL_ADOPTION_REQUIRED","message": "native-context-model-missing","facts": {"path": "diagnostic","category": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
240
+ "native-context-model-invalid": {"code": "AC_SCHEMA_INVALID","message": "native-context-model-invalid","facts": {"path": "diagnostic","invariant": "diagnostic","issues": "issues","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
241
+ "native-context-budget-invalid": {"code": "AC_SCHEMA_INVALID","message": "native-context-budget-invalid","facts": {"path": "diagnostic","field": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
242
+ "native-context-profile-incomplete": {"code": "AC_SCHEMA_INVALID","message": "native-context-profile-incomplete","facts": {"nodeId": "diagnostic","field": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
243
+ "native-context-text-invalid": {"code": "AC_SCHEMA_INVALID","message": "native-context-text-invalid","facts": {"path": "diagnostic","encoding": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
244
+ "native-context-input-ambiguous": {"code": "AC_SCHEMA_INVALID","message": "native-context-input-ambiguous","facts": {"path": "diagnostic","branch": "diagnostic","key": "diagnostic","offset": "uint","omittedKeyCharacters": "uint","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
245
+ "native-context-path-unsafe": {"code": "AC_PATH_DENIED","message": "native-context-path-unsafe","facts": {"path": "diagnostic","category": "diagnostic","issues": "issues","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input-path","retryable": false},
246
+ "native-context-entry-kind-unsupported": {"code": "AC_PATH_DENIED","message": "native-context-entry-kind-unsupported","facts": {"path": "diagnostic","mode": "diagnostic","kind": "diagnostic","category": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input-path","retryable": false},
247
+ "native-context-ref-unsafe": {"code": "AC_PATH_DENIED","message": "native-context-ref-unsafe","facts": {"selector": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input-path","retryable": false},
248
+ "native-context-ref-unavailable": {"code": "AC_PRECONDITION_FAILED","message": "native-context-ref-unavailable","facts": {"selector": "diagnostic","category": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
249
+ "native-context-blob-unavailable": {"code": "AC_PRECONDITION_FAILED","message": "native-context-blob-unavailable","facts": {"category": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
250
+ "native-context-input-missing": {"code": "AC_PRECONDITION_FAILED","message": "native-context-input-missing","facts": {"path": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
251
+ "native-context-input-unreadable": {"code": "AC_PRECONDITION_FAILED","message": "native-context-input-unreadable","facts": {"path": "diagnostic","category": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
252
+ "native-context-input-changed": {"code": "AC_PRECONDITION_FAILED","message": "native-context-input-changed","facts": {"category": "diagnostic","changedPaths": "diagnosticList","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "rerun-native-export","retryable": true},
253
+ "native-document-marker-invalid": {"code": "AC_SCHEMA_INVALID","message": "native-document-marker-invalid","facts": {"path": "diagnostic","targetId": "diagnostic","diagnostics": "diagnostics","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
254
+ "native-document-section-ambiguous": {"code": "AC_SCHEMA_INVALID","message": "native-document-section-ambiguous","facts": {"path": "diagnostic","targetId": "diagnostic","section": "diagnostic","headingCount": "uint","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false},
255
+ "native-context-limit-exceeded": {"code": "AC_PRECONDITION_FAILED","message": "native-context-limit-exceeded","facts": {"limit": "diagnostic","maximum": "uint","observed": "uint","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "reduce-native-input-size","retryable": false},
256
+ "native-context-git-read-failed": {"code": "AC_PRECONDITION_FAILED","message": "native-context-git-read-failed","facts": {"operation": "diagnostic","category": "diagnostic","omittedDiagnosticCount": "uint","omittedDiagnosticCharacters": "uint"},"native": true,"action": "repair-native-input","retryable": false}
257
+ } as Record<string, Policy>);
258
+ Object.assign(policies, {
259
+ "refactor-scan-invalid": {code: "AC_SCHEMA_INVALID", message: "The refactor scan could not produce a valid assessment.", facts: {}},
260
+ "refactor-model-unreadable": {code: "AC_MODEL_ADOPTION_REQUIRED", message: "The refactor scan requires a readable adopted model.", facts: {}},
261
+ "refactor-request-head-changed": {code: "AC_REFACTOR_STALE", message: "The requested HEAD differs from the measured HEAD.", facts: {expectedHeadSha: "head", actualHeadSha: "head"}},
262
+ "refactor-request-worktree-changed": {code: "AC_REFACTOR_STALE", message: "The requested worktree differs from the measured worktree.", facts: {expectedWorktreeDigest: "sha256", actualWorktreeDigest: "sha256"}},
263
+ "refactor-measurement-changed": {"code": "AC_REFACTOR_STALE","facts": {"changedFields": "changedFields"},"message": "Refactor measurement changed."},
264
+ "audit-issue-list-inconclusive": {"code": "AC_PRECONDITION_FAILED","facts": {"runId": "id"},"message": "Audit issue list inconclusive."},
265
+ "audit-issue-create-inconclusive": {"code": "AC_PRECONDITION_FAILED","facts": {"runId": "id","draftId": "id"},"message": "Audit issue create inconclusive."}
266
+ } as Record<string, Policy>);
267
+ Object.assign(policies, {
268
+ "projection-accepted-change-committed": {code: "AC_PROJECTION_APPLY_COMMITTED", recovery: true, message: "This accepted change is already committed. When available, send error.details.readbackRequest unchanged to projection readback; use the original identities for explicit recovery."},
269
+ "projection-apply-request-differs": {code: "AC_PROJECTION_APPLY_COMMITTED", recovery: true, message: "This requestId already committed a different request. Use a new requestId for new work; read back the committed result with error.details.readbackRequest when available."},
270
+ "projection-apply-request-digest-unrecorded": {code: "AC_PROJECTION_APPLY_COMMITTED", recovery: true, message: "This requestId committed before request digests were recorded and cannot be reused. Use a new requestId for new work. Use error.details.readbackRequest when available; otherwise inspect the original receipt manually."},
271
+ "projection-apply-committed-manual-only": {code: "AC_PROJECTION_APPLY_COMMITTED", recovery: true, message: "This apply is committed. Its complete readback metadata exceeds the details budget. Automatic restore and replay are refused; inspect the original receipt using the complete lookupKey and verify applyId for manual maintenance."},
272
+ "projection-apply-recovery-metadata-too-large": {code: "AC_PRECONDITION_FAILED", facts: {measuredBytes: "uint", limitBytes: "uint"}, message: "The final projection recovery metadata exceeds its byte budget. Reduce the accepted change or changed paths before applying; no business write was started."}
273
+ } as Record<string, Policy>);
274
+ for (const policy of Object.values(policies)) {
275
+ if (policy.facts) { policy.fields = Object.freeze(Object.keys(policy.facts)); Object.freeze(policy.facts); }
276
+ if (policy.fields) Object.freeze(policy.fields);
277
+ Object.freeze(policy);
278
+ }
279
+ export const FAILURE_POLICIES: Readonly<Record<string, Readonly<Policy>>> = Object.freeze(policies);
280
+ const catalogDefaults = Object.fromEntries(Object.entries(ERROR_CATALOG).map(([code, value]) => [code, Object.freeze({ severity: value.severity, retryable: value.retryable, action: value.action })])) as Readonly<Record<ArchContextErrorCode, Readonly<Pick<StrictFailure, "severity" | "retryable" | "action">>>>;
281
+ const actions = new Set([...Object.values(catalogDefaults).map(value => value.action), "correct-command-arguments", "inspect-diagnostics", "reconcile-before-retry", ...Object.values(policies).flatMap(policy => policy.action ? [policy.action] : [])]);
282
+ const bytes = (value: string) => new TextEncoder().encode(value).byteLength;
283
+ const record = (value: unknown): value is Record<string, unknown> => value !== null && typeof value === "object" && !Array.isArray(value);
284
+ const finiteNonnegative = (value: unknown): value is number => typeof value === "number" && Number.isFinite(value) && value >= 0;
285
+ const criticalKey = (key: string) => /id$|digest|receipt|binding|readback|lookupkey/i.test(key);
286
+ /** Read a single own data value without executing accessors or accepting prototype metadata. */
287
+ function ownData(input: unknown, key: string): unknown {
288
+ if (!record(input)) throw new Error("Failure metadata must be an object");
289
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
290
+ if (!descriptor) {
291
+ if (key in input) throw new Error("Failure metadata must use own data properties");
292
+ return undefined;
293
+ }
294
+ if (!("value" in descriptor)) throw new Error("Failure metadata cannot use accessors");
295
+ return descriptor.value;
296
+ }
297
+
298
+ function fact(key: string, value: unknown): Json | undefined {
299
+ if (key === "stage") return typeof value === "string" && (FAILURE_STAGES as readonly string[]).includes(value) ? value : undefined;
300
+ if (key === "checks") {
301
+ if (!Array.isArray(value)) return undefined;
302
+ const length = Object.getOwnPropertyDescriptor(value, "length")?.value;
303
+ if (!Number.isSafeInteger(length) || length < 0 || length > 32) return undefined;
304
+ const checks: string[] = [];
305
+ for (let index = 0; index < length; index++) {
306
+ const entry = Object.getOwnPropertyDescriptor(value, String(index));
307
+ if (!entry || !("value" in entry) || (entry.value !== "ownership" && entry.value !== "anchors")) return undefined;
308
+ checks.push(entry.value);
309
+ }
310
+ return checks;
311
+ }
312
+ if (key === "issueCount") return finiteNonnegative(value) && Number.isSafeInteger(value) ? value : undefined;
313
+ if (key === "method") return typeof value === "string" && ["health", "practiceCheckpoint", "completeTask"].includes(value) ? value : undefined;
314
+ if (key === "timeoutMs" || key === "elapsedMs") return finiteNonnegative(value) ? value : undefined;
315
+ if (key === "mutationOutcome") return value === "unknown" ? value : undefined;
316
+ return undefined;
317
+ }
318
+ function detailsFor(policy: Policy, input: unknown): { [key: string]: Json } {
319
+ if (policy.native) return nativeFactsFor(policy, input);
320
+ if (policy.facts) return localFactsFor(policy.facts, input);
321
+ const result: { [key: string]: Json } = {};
322
+ if (!record(input)) throw new Error("Failure details must be an object");
323
+ let omitted = 0;
324
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(input))) {
325
+ if (criticalKey(key)) throw new Error("Failure metadata contains an unsupported critical binding");
326
+ if (key === "omittedCount" && policy.code !== "AC_UNCLASSIFIED_FAILURE" && finiteNonnegative(descriptor.value) && Number.isSafeInteger(descriptor.value)) {
327
+ omitted += descriptor.value;
328
+ continue;
329
+ }
330
+ if (key === "stage" || policy.fields?.includes(key)) {
331
+ const value = "value" in descriptor ? fact(key, descriptor.value) : undefined;
332
+ if (value !== undefined) { result[key] = value; continue; }
333
+ }
334
+ // Unknown compound facts may hide an essential recovery binding. Refuse without traversing caller data.
335
+ if ("value" in descriptor && descriptor.value !== null && typeof descriptor.value === "object") throw new Error("Failure metadata contains an unsupported compound fact");
336
+ omitted++;
337
+ }
338
+ if (omitted > 0 && policy.code !== "AC_UNCLASSIFIED_FAILURE") result.omittedCount = Math.min(omitted, 1_000_000);
339
+ return result;
340
+ }
341
+
342
+ /** Closed C1 facts retain complete bindings. Unsupported compound/critical fields never disappear. */
343
+ function dataRecord(value: unknown): Record<string, PropertyDescriptor> {
344
+ if (!record(value)) throw new Error("Facts must be a plain record");
345
+ const proto = Object.getPrototypeOf(value), keys = Reflect.ownKeys(value);
346
+ if ((proto !== Object.prototype && proto !== null) || keys.length > 16 || Object.keys(value).length !== keys.length || keys.some(key => typeof key !== "string")) throw new Error("Facts violate plain own-data contract");
347
+ const fields = Object.getOwnPropertyDescriptors(value);
348
+ if (Object.values(fields).some(field => !("value" in field))) throw new Error("Facts cannot use accessors");
349
+ return fields;
350
+ }
351
+ const localEnums: Readonly<Record<string, readonly string[]>> = {
352
+ reviewWorktreeRefusal: ["UNKNOWN","HEAD_UNAVAILABLE","HEAD_SHA_MISMATCH","TREE_OID_MISMATCH","WORKTREE_NOT_DETACHED","WORKTREE_NOT_CLEAN"],
353
+ reviewOwnershipRefusal: ["persisted-manifest-missing","persisted-manifest-identity-mismatch","run-id","manifest-path","lock-path","persisted-manifest-mismatch","persisted-lock-mismatch","run-root-name","worktree-temp-root","codegraph-temporary-state-root","run-root-not-a-directory","run-root-owner-marker-missing","run-root-owner-marker-mismatch","worktree-temporary-root","worktree-root","worktree-source-root"],
354
+ projectionInputField: ["headSha","worktreeDigest","sourceFiles"],
355
+ upgradeAction: ["upgrade-archctx-runtime"],
356
+ upgradeCommand: ["archctx daemon upgrade"],
357
+ ownershipAction: ["remove-whole-file-outputs-and-reapply"],
358
+ ledgerModeField: ["rolloutMode","readMode","writeMode"],
359
+ depsField: ["from", "into", "importNodeLimit", "edgeLimit"],
360
+ depsIssue: ["unknown-node", "invalid-limit", "missing-value"],
361
+ snapshotField: ["repositoryId", "workspaceId", "headSha", "worktreeDigest"],
362
+ jobsField: ["workerId"],
363
+ observedDomain: ["observed"],
364
+ inputAvailability: ["unavailable"],
365
+ unclassifiedCause: ["unclassified"],
366
+ evidenceObservation: ["before-resolve-append"],
367
+ "searched": [
368
+ "recorded",
369
+ "recorded-or-scanned"
370
+ ],
371
+ "decisionStatus": [
372
+ "acknowledged",
373
+ "accepted",
374
+ "deferred",
375
+ "rejected",
376
+ "resolved",
377
+ "waived"
378
+ ],
379
+ "disposition": [
380
+ "not_improved",
381
+ "partially_resolved",
382
+ "regressed",
383
+ "resolved",
384
+ "stale"
385
+ ],
386
+ "consentReason": [
387
+ "not-granted",
388
+ "unreadable",
389
+ "repository-mismatch",
390
+ "origin-mismatch",
391
+ "egress-policy-changed"
392
+ ],
393
+ "context7Kind": [
394
+ "http-error",
395
+ "timeout",
396
+ "rate-limited",
397
+ "malformed",
398
+ "transport-error",
399
+ "circuit-open"
400
+ ]
401
+ };
402
+ function localFactValue(rule: string, value: unknown): Json {
403
+ if (rule === "commandSuggestion" && (value === null || (typeof value === "string" && bytes(value) <= 1024 && /^archctx (recommendations |refactor verify )[^\r\n]*$/.test(value)))) return value;
404
+ if (rule === "publicConfirmationToken" && typeof value === "string" && bytes(value) <= 1024 && /^public:github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+:[a-f0-9]{40}:[A-Za-z0-9_.:-]+$/.test(value)) return value;
405
+ if (rule === "diagnosticPaths" || rule === "diagnosticCommands") {
406
+ if (!Array.isArray(value)) throw new Error("Diagnostics must be an own list");
407
+ const length=Object.getOwnPropertyDescriptor(value,"length")?.value;
408
+ if (!Number.isSafeInteger(length) || length > (rule === "diagnosticPaths" ? 8 : 2) || Reflect.ownKeys(value).length!==length+1) throw new Error("Diagnostics exceed their closed budget");
409
+ return Array.from({length},(_,index)=>{const field=Object.getOwnPropertyDescriptor(value,String(index));if(!field || !("value" in field) || typeof field.value!=="string" || bytes(field.value)>(rule === "diagnosticPaths" ? 256 : 1024)) throw new Error("Invalid diagnostic");return field.value;});
410
+ }
411
+ if (rule === "boolean" && typeof value === "boolean") return value;
412
+ if (rule === "uint" && finiteNonnegative(value) && Number.isSafeInteger(value)) return value;
413
+ if (rule === "changedFields") {
414
+ if (!Array.isArray(value)) throw new Error("Changed fields must be a list");
415
+ const length = Object.getOwnPropertyDescriptor(value, "length")?.value;
416
+ if (!Number.isSafeInteger(length) || length > 2 || Reflect.ownKeys(value).length !== length + 1) throw new Error("Changed fields exceed schema");
417
+ return Array.from({length}, (_, index) => {
418
+ const field = Object.getOwnPropertyDescriptor(value, String(index));
419
+ if (!field || !("value" in field) || (field.value !== "headSha" && field.value !== "worktreeDigest")) throw new Error("Changed field is invalid");
420
+ return field.value;
421
+ });
422
+ }
423
+ if (rule === "rpcVersion" && typeof value === "string" && bytes(value) <= 256 && (value === "unknown" || /^archcontext\.runtime-rpc\/v[0-9]+$/.test(value))) return value;
424
+ if (typeof value === "string" && bytes(value) <= 256) {
425
+ if (rule === "id" && value.length > 0) return value;
426
+ if (rule === "sha256" && /^sha256:[a-f0-9]{64}$/.test(value)) return value;
427
+ if (rule === "head" && /^[a-f0-9]{40}$/.test(value)) return value;
428
+ if (localEnums[rule]?.includes(value)) return value;
429
+ }
430
+ throw new Error("Fact violates its closed schema");
431
+ }
432
+ function localFactsFor(schema: Readonly<Record<string, string>>, input: unknown): {[key: string]: Json} {
433
+ const fields = dataRecord(input), result: {[key: string]: Json} = {};
434
+ let omitted = 0;
435
+ for (const [key, rule] of Object.entries(schema)) {
436
+ const optional = rule.startsWith("optional:");
437
+ if (!Object.hasOwn(fields, key)) { if (optional) continue; throw new Error("Required fact is absent"); }
438
+ result[key] = localFactValue(optional ? rule.slice("optional:".length) : rule, fields[key].value);
439
+ }
440
+ for (const [key, field] of Object.entries(fields)) {
441
+ if (Object.hasOwn(schema, key)) continue;
442
+ if (key === "omittedCount" && finiteNonnegative(field.value) && Number.isSafeInteger(field.value)) { omitted += field.value; continue; }
443
+ if (criticalKey(key) || (field.value !== null && typeof field.value === "object")) throw new Error("Unsupported critical or compound fact");
444
+ omitted++;
445
+ }
446
+ if (omitted) result.omittedCount = Math.min(omitted, 1_000_000);
447
+ return result;
448
+ }
449
+ const nativeEnums: Readonly<Record<string, readonly string[]>> = {
450
+ "category": [
451
+ "unknown-flag",
452
+ "unexpected-positional",
453
+ "timeout",
454
+ "unavailable",
455
+ "ambiguous",
456
+ "ambiguous-tree-path",
457
+ "descriptor-content",
458
+ "descriptor-identity",
459
+ "duplicate",
460
+ "exactly-one-selector-required",
461
+ "export-read",
462
+ "filesystem-path-alias",
463
+ "gitlink",
464
+ "head-or-tree",
465
+ "input-digest",
466
+ "input-identity",
467
+ "invalid-metadata-size",
468
+ "invalid-oid",
469
+ "invalid-request-oid",
470
+ "invalid-size",
471
+ "invalid-tree-oid",
472
+ "link",
473
+ "malformed-header",
474
+ "malformed-record",
475
+ "metadata-count-mismatch",
476
+ "metadata-size-mismatch",
477
+ "missing-header",
478
+ "missing-object-or-metadata-mismatch",
479
+ "missing-terminator",
480
+ "missing-value",
481
+ "model-layout",
482
+ "model-membership",
483
+ "model-path-encoding",
484
+ "nested-repository",
485
+ "no-native-nodes",
486
+ "non-regular",
487
+ "object-content-mismatch",
488
+ "oid-or-type-mismatch",
489
+ "open-or-read",
490
+ "path-encoding-or-alias",
491
+ "path-identity",
492
+ "path-presence-or-identity",
493
+ "repository-boundary-stat",
494
+ "second-observation-invalid",
495
+ "size-mismatch",
496
+ "stat",
497
+ "tree-unavailable",
498
+ "truncated-body-or-terminator",
499
+ "unexpected-trailing-bytes",
500
+ "unsupported-operation",
501
+ "unsupported-read-arguments",
502
+ "windows-path-alias"
503
+ ],
504
+ "field": [
505
+ "$",
506
+ "extensions.contractFiles",
507
+ "extensions.lspProfile",
508
+ "extensions.verification",
509
+ "identity-or-contractFiles",
510
+ "layout-target-identity",
511
+ "runtime.contextBudgetBytes"
512
+ ],
513
+ "encoding": [
514
+ "utf-8"
515
+ ],
516
+ "section": [
517
+ "section3",
518
+ "backlog"
519
+ ],
520
+ "selector": [
521
+ "ref"
522
+ ],
523
+ "supportedProfile": ["repo-harness/v1"],
524
+ "supportedFormat": [
525
+ "json"
526
+ ],
527
+ "operation": [
528
+ "rev-parse",
529
+ "ls-tree",
530
+ "ls-files",
531
+ "cat-file"
532
+ ],
533
+ "limit": [
534
+ "batch-response-bytes",
535
+ "batchOverheadBytes",
536
+ "fileBytes",
537
+ "git-read-output",
538
+ "gitTimeoutMs",
539
+ "inputBytes",
540
+ "inputFiles",
541
+ "responseBytes",
542
+ "treeListingBytes"
543
+ ],
544
+ "kind": [
545
+ "marker-syntax-or-attributes",
546
+ "marker-nested",
547
+ "marker-duplicate",
548
+ "marker-unmatched-end",
549
+ "marker-target-mismatch",
550
+ "marker-unmatched-begin",
551
+ "unexpected-target",
552
+ "unsupported-renderer",
553
+ "output-digest-mismatch"
554
+ ]
555
+ };
556
+ const nativeDiagnosticKinds = new Set(nativeEnums.kind);
557
+ /** Native fields are diagnostics, not recovery authority. Omit complete items, never truncate an ID. */
558
+ function nativeFactsFor(policy: Policy, input: unknown): {[key: string]: Json} {
559
+ const fields = dataRecord(input), result: {[key: string]: Json} = {};
560
+ let omitted = 0, characters = 0;
561
+ const omit = (value: Json) => {
562
+ omitted++;
563
+ const strings = (v: Json): number => typeof v === "string" ? v.length : Array.isArray(v) ? v.reduce<number>((n, item) => n + strings(item), 0) : record(v) ? Object.values(v).reduce<number>((n, item) => n + strings(item as Json), 0) : 0;
564
+ characters += strings(value);
565
+ };
566
+ const text = (value: unknown): string => { if (typeof value !== "string") throw new Error("Native diagnostic must be a primitive string"); return value; };
567
+ const snapshotItem = (value: unknown, type: string): Json | undefined => {
568
+ if (type === "diagnostic") { const s = text(value); if (bytes(s) > 256) { omit(s); return undefined; } return s; }
569
+ const item = dataRecord(value);
570
+ if (type === "issues") {
571
+ if (Object.keys(item).length !== 3 || !["entityId", "field", "invariant"].every(key => Object.hasOwn(item, key))) throw new Error("Native issue schema is invalid");
572
+ const clean = Object.fromEntries(Object.entries(item).map(([key, data]) => [key, text(data.value)]));
573
+ if (Object.values(clean).some(v => bytes(v) > 256)) { omit(clean); return undefined; }
574
+ return clean;
575
+ }
576
+ if (Object.keys(item).length !== 2 || !Object.hasOwn(item, "kind") || !Object.hasOwn(item, "range") || !nativeDiagnosticKinds.has(item.kind.value)) throw new Error("Native diagnostic schema is invalid");
577
+ const range = dataRecord(item.range.value);
578
+ if (Object.keys(range).length !== 4 || !["startByte", "endByte", "startLine", "endLine"].every(key => Object.hasOwn(range, key))) throw new Error("Native range schema is invalid");
579
+ return {kind: item.kind.value, range: Object.fromEntries(Object.entries(range).map(([key, d]) => [key, localFactValue("uint", d.value)]))};
580
+ };
581
+ for (const [key, field] of Object.entries(fields)) {
582
+ const rule = policy.facts && Object.hasOwn(policy.facts, key) ? policy.facts[key] : undefined;
583
+ if (!rule) {
584
+ if (criticalKey(key) || (field.value !== null && typeof field.value === "object")) throw new Error("Unknown native compound/identity fact");
585
+ omit(typeof field.value === "string" ? field.value : null); continue;
586
+ }
587
+ if (key === "omittedDiagnosticCount" || key === "omittedDiagnosticCharacters") {
588
+ const n = localFactValue("uint", field.value) as number;
589
+ if (key === "omittedDiagnosticCount") omitted += n; else characters += n;
590
+ continue;
591
+ }
592
+ if (rule === "uint") {
593
+ if (key === "observed" && typeof field.value === "number" && Number.isFinite(field.value) && (!Number.isSafeInteger(field.value) || field.value < 0)) { omitted++; continue; }
594
+ result[key] = localFactValue(rule, field.value); continue;
595
+ }
596
+ if (rule === "diagnostic") {
597
+ const value = snapshotItem(field.value, rule);
598
+ if (value !== undefined) {
599
+ if (key !== "kind" && nativeEnums[key] && !nativeEnums[key].includes(value as string)) throw new Error("Native diagnostic enum is invalid");
600
+ result[key] = value;
601
+ }
602
+ continue;
603
+ }
604
+ if (!Array.isArray(field.value)) throw new Error("Native diagnostic list is invalid");
605
+ const list = field.value, length = Object.getOwnPropertyDescriptor(list, "length")?.value;
606
+ if (!Number.isSafeInteger(length) || length < 0) throw new Error("Native diagnostic length is invalid");
607
+ const keys = Reflect.ownKeys(list);
608
+ if (keys.length !== length + 1 || keys.some(k => k !== "length" && (typeof k !== "string" || !/^(0|[1-9][0-9]*)$/.test(k)))) throw new Error("Native diagnostic list must be dense own data");
609
+ const values: Json[] = [];
610
+ for (let i = 0; i < length; i++) {
611
+ const d = Object.getOwnPropertyDescriptor(list, String(i));
612
+ if (!d || !("value" in d)) throw new Error("Native diagnostic list cannot use accessors");
613
+ if (i >= 32) { omitted++; continue; }
614
+ const value = snapshotItem(d.value, rule === "diagnosticList" ? "diagnostic" : rule);
615
+ if (value !== undefined) values.push(value);
616
+ }
617
+ result[key] = values;
618
+ }
619
+ const counts = () => {
620
+ if (!Number.isSafeInteger(omitted) || !Number.isSafeInteger(characters)) throw new Error("Native diagnostic counts exceed schema");
621
+ if (omitted) result.omittedDiagnosticCount = omitted;
622
+ if (characters) result.omittedDiagnosticCharacters = characters;
623
+ };
624
+ const fits = () => {
625
+ counts();
626
+ const base = catalogDefaults[policy.code as ArchContextErrorCode];
627
+ const error = {code: policy.code, message: policy.message, severity: base.severity, retryable: policy.retryable ?? base.retryable, action: policy.action ?? base.action, reasonCode: Object.entries(policies).find(([, p]) => p === policy)?.[0], details: result};
628
+ const envelope = {schemaVersion: "archcontext.envelope/v1", ok: false, requestId: "export.native", error};
629
+ return bytes(JSON.stringify(result)) <= 8192 && bytes(JSON.stringify(envelope, null, 2) + "\n") <= 8192;
630
+ };
631
+ while (!fits()) {
632
+ const listKey = Object.keys(result).reverse().find(key => Array.isArray(result[key]) && (result[key] as Json[]).length > 0);
633
+ if (listKey) { omit((result[listKey] as Json[]).pop()!); continue; }
634
+ const key = Object.keys(result).reverse().find(key => typeof result[key] === "string");
635
+ if (!key) throw new Error("Native metadata cannot fit envelope budget");
636
+ omit(result[key]); delete result[key];
637
+ }
638
+ return result;
639
+ }
640
+
641
+ function budgetIssues(value: unknown, depth = 0, maxStringBytes = 256): string[] {
642
+ if (depth > 4) return ["details depth exceeds four"];
643
+ if (typeof value === "string") return bytes(value) <= maxStringBytes ? [] : ["detail string exceeds UTF-8 budget"];
644
+ if (typeof value === "number") return Number.isFinite(value) ? [] : ["detail number is not finite"];
645
+ if (Array.isArray(value)) return value.length <= 32 ? value.flatMap(item => budgetIssues(item, depth + 1, maxStringBytes)) : ["detail list exceeds 32"];
646
+ if (record(value)) return Object.keys(value).length <= 16 ? Object.values(value).flatMap(item => budgetIssues(item, depth + 1, maxStringBytes)) : ["detail object exceeds 16 fields"];
647
+ return value === null || typeof value === "boolean" ? [] : ["detail value is not JSON"];
648
+ }
649
+ type Attributes = Partial<Pick<StrictFailure, "action" | "severity" | "retryable">>;
650
+ export function knownFailure<C extends FailureCode>(code: C, reasonCode: string, details: unknown = {}, attributes: Attributes = {}): StrictFailure<C> {
651
+ // TypeScript types do not protect this runtime boundary. Do not coerce policy keys.
652
+ if (typeof code !== "string" || typeof reasonCode !== "string") throw new Error("Failure code/reason must be primitive strings");
653
+ const suppliedAction = ownData(attributes, "action");
654
+ const suppliedSeverity = ownData(attributes, "severity");
655
+ const suppliedRetryable = ownData(attributes, "retryable");
656
+ if ((suppliedAction !== undefined && typeof suppliedAction !== "string")
657
+ || (suppliedSeverity !== undefined && typeof suppliedSeverity !== "string")
658
+ || (suppliedRetryable !== undefined && typeof suppliedRetryable !== "boolean")) throw new Error("Invalid failure attribute types");
659
+ const policy = Object.hasOwn(FAILURE_POLICIES, reasonCode) ? FAILURE_POLICIES[reasonCode] : undefined;
660
+ if (!policy || policy.code !== code) throw new Error("Failure code/reason pair is not registered");
661
+ const catalog = code === "RPC_TIMEOUT" || code === "RPC_ABORTED"
662
+ ? { severity: "error" as const, retryable: false, action: "reconcile-before-retry" }
663
+ : catalogDefaults[code as ArchContextErrorCode];
664
+ const action = (suppliedAction as string | undefined) ?? policy.action ?? catalog.action;
665
+ const severity = (suppliedSeverity as Severity | undefined) ?? catalog.severity;
666
+ const retryable = (suppliedRetryable as boolean | undefined) ?? policy.retryable ?? catalog.retryable;
667
+ if (!actions.has(action) || !["notice", "warning", "error", "critical"].includes(severity) || typeof retryable !== "boolean") throw new Error("Invalid failure attributes");
668
+ if ((code === "RPC_TIMEOUT" || code === "RPC_ABORTED") && retryable) throw new Error("Transport failures cannot request automatic retry");
669
+ if (code === "AC_UNCLASSIFIED_FAILURE" && (action !== "inspect-diagnostics" || severity !== "error" || retryable)) throw new Error("Unclassified failure attributes are fixed");
670
+ if (policy.journalRecovery) {
671
+ if (action !== catalog.action || severity !== catalog.severity || retryable !== catalog.retryable) throw new Error("Recovery attributes are fixed");
672
+ return {code, message:policy.message, severity, retryable, action, reasonCode, details:changeSetRecoveryDetailsSnapshot(details)};
673
+ }
674
+ if (policy.recovery) {
675
+ if (action !== catalog.action || severity !== catalog.severity || retryable !== catalog.retryable) throw new Error("Committed recovery attributes are fixed");
676
+ return { code, message: policy.message, severity, retryable, action, reasonCode, details: projectionRecoveryDetailsSnapshot(reasonCode, details) };
677
+ }
678
+ const safeDetails = detailsFor(policy, details);
679
+ const extendedFacts = Object.values(policy.facts ?? {}).some(rule => rule === "publicConfirmationToken" || rule === "commandSuggestion" || rule === "diagnosticCommands");
680
+ if (budgetIssues(safeDetails, 0, extendedFacts ? 1024 : 256).length || bytes(JSON.stringify(safeDetails)) > 8192 || bytes(policy.message) > 1024) throw new Error("Failure metadata exceeds its UTF-8 budget");
681
+ return { code, message: policy.message, severity, retryable, action, reasonCode, details: safeDetails };
682
+ }
683
+ /** Snapshot caller-owned fields once, before validation or use. Missing legacy details stay optional. */
684
+ function failureFromData<C extends FailureCode = FailureCode>(input: unknown): StrictFailure<C> {
685
+ const code = ownData(input, "code");
686
+ const reasonCode = ownData(input, "reasonCode");
687
+ const details = ownData(input, "details");
688
+ const action = ownData(input, "action");
689
+ const severity = ownData(input, "severity");
690
+ const retryable = ownData(input, "retryable");
691
+ return knownFailure(code as C, reasonCode as string, details, {
692
+ action: action as string | undefined, severity: severity as Severity | undefined, retryable: retryable as boolean | undefined
693
+ });
694
+ }
695
+ export function unclassifiedFailure(stage?: FailureStage): StrictFailure<"AC_UNCLASSIFIED_FAILURE"> {
696
+ return knownFailure("AC_UNCLASSIFIED_FAILURE", "failure-unclassified", stage ? { stage } : {});
697
+ }
698
+ export function failureEnvelope<C extends FailureCode>(requestId: string, failure: StrictFailure<C>): FailureEnvelope<C> {
699
+ // Revalidate/copy even typed metadata so caller mutation cannot bypass the closed contract.
700
+ if (typeof requestId !== "string" || bytes(requestId) > 256) throw new Error("Failure surface identity exceeds budget");
701
+ const result: FailureEnvelope<C> = { schemaVersion: "archcontext.envelope/v1", ok: false, requestId, error: failureFromData<C>(failure) };
702
+ if (!FAILURE_POLICIES[result.error.reasonCode]?.recovery && !FAILURE_POLICIES[result.error.reasonCode]?.journalRecovery && (bytes(JSON.stringify(result)) > 8192 || bytes(JSON.stringify(result, null, 2) + "\n") > 8192)) throw new Error("Failure envelope exceeds budget");
703
+ return result;
704
+ }
705
+ /** Public value readers retain their success signature and throw this typed failure. */
706
+ export class FailureError<C extends FailureCode = ArchContextErrorCode> extends Error {
707
+ readonly failure: StrictFailure<C>;
708
+ constructor(code: C, reasonCode: string, details: unknown = {}, attributes: Attributes = {}) {
709
+ const failure = knownFailure(code, reasonCode, details, attributes);
710
+ super(failure.message);
711
+ this.name = "FailureError";
712
+ this.failure = failure;
713
+ }
714
+ get code(): C { return this.failure.code; }
715
+ get reasonCode(): string { return this.failure.reasonCode; }
716
+ get action(): string { return this.failure.action; }
717
+ get severity(): Severity { return this.failure.severity; }
718
+ get retryable(): boolean { return this.failure.retryable; }
719
+ get details(): { [key: string]: Json } { return this.failure.details; }
720
+ }
721
+ export function toEnvelope(requestId: string, error: unknown, stage: FailureStage): FailureEnvelope {
722
+ try {
723
+ return failureEnvelope(requestId, error instanceof FailureError ? ownData(error, "failure") as StrictFailure : unclassifiedFailure(stage));
724
+ } catch {
725
+ return failureEnvelope(requestId, unclassifiedFailure("failure-metadata"));
726
+ }
727
+ }
728
+ /** Successful results are returned by identity. Only selected surface callers use this adapter. */
729
+ export function normalizeFailureResult<T>(result: T, stage: FailureStage, requestId = "failure"): T | FailureEnvelope {
730
+ if (!record(result)) return result;
731
+ try {
732
+ if (ownData(result, "ok") !== false) return result;
733
+ return failureEnvelope(requestId, failureFromData(ownData(result, "error")));
734
+ } catch {
735
+ return toEnvelope(requestId, undefined, "failure-metadata");
736
+ }
737
+ }
738
+ /** Bounded JSON copy for strict validation; never stringify caller-owned objects or run their hooks. */
739
+ function jsonDataSnapshot(value: unknown, depth = 0, maxStringBytes = 256): Json {
740
+ if (depth > 4) throw new Error("Failure details exceed depth budget");
741
+ if (value === null || typeof value === "boolean") return value;
742
+ if (typeof value === "string" && bytes(value) <= maxStringBytes) return value;
743
+ if (typeof value === "number" && Number.isFinite(value)) return value;
744
+ if (Array.isArray(value)) {
745
+ const length = Object.getOwnPropertyDescriptor(value, "length")?.value;
746
+ if (!Number.isSafeInteger(length) || length > 32 || Reflect.ownKeys(value).length !== length + 1) throw new Error("Failure details violate array contract");
747
+ return Array.from({ length }, (_, index) => {
748
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
749
+ if (!descriptor || !("value" in descriptor)) throw new Error("Failure details cannot use accessors or sparse lists");
750
+ return jsonDataSnapshot(descriptor.value, depth + 1, maxStringBytes);
751
+ });
752
+ }
753
+ if (record(value)) {
754
+ const prototype = Object.getPrototypeOf(value);
755
+ const keys = Reflect.ownKeys(value);
756
+ if ((prototype !== Object.prototype && prototype !== null) || keys.length > 16 || Object.keys(value).length !== keys.length || keys.some(key => typeof key !== "string")) throw new Error("Failure details violate object contract");
757
+ return Object.fromEntries((keys as string[]).map(key => [key, jsonDataSnapshot(ownData(value, key), depth + 1, maxStringBytes)]));
758
+ }
759
+ throw new Error("Failure details are not bounded JSON data");
760
+ }
761
+ export function failureIssues(value: unknown): string[] {
762
+ try {
763
+ if (!record(value)) return ["failure must be an object"];
764
+ const fields = ["code", "message", "severity", "retryable", "action", "reasonCode", "details"];
765
+ const keys = Reflect.ownKeys(value);
766
+ const prototype = Object.getPrototypeOf(value);
767
+ if ((prototype !== Object.prototype && prototype !== null) || keys.length !== fields.length || Object.keys(value).length !== fields.length || keys.some(key => typeof key !== "string" || !fields.includes(key))) return ["failure must match its closed registered contract"];
768
+ const data = Object.fromEntries(fields.map(key => [key, ownData(value, key)]));
769
+ const policy = typeof data.reasonCode === "string" ? policies[data.reasonCode] : undefined;
770
+ const extendedFacts = policy !== undefined && policy.code === data.code && Object.values(policy.facts ?? {}).some(rule => rule === "publicConfirmationToken" || rule === "commandSuggestion" || rule === "diagnosticCommands");
771
+ data.details = policy?.journalRecovery && policy.code === data.code ? changeSetRecoveryDetailsSnapshot(data.details) : policy?.recovery && policy.code === data.code ? projectionRecoveryDetailsSnapshot(data.reasonCode as string, data.details) : jsonDataSnapshot(data.details, 0, extendedFacts ? 1024 : 256);
772
+ if (typeof data.message !== "string" || bytes(data.message) > 1024 || bytes(JSON.stringify(data.details)) > (policy?.recovery || policy?.journalRecovery ? PROJECTION_RECOVERY_DETAILS_LIMIT_BYTES : 8192)) return ["failure exceeds byte/shape budget"];
773
+ const expected = failureFromData(data);
774
+ return canonicalize(data as Json) === canonicalize(expected as unknown as Json) ? [] : ["failure must match its closed registered contract"];
775
+ } catch { return ["failure violates its registered contract or budget"]; }
776
+ }