From 2cca3886b860638c7675d4fbf313db46cda68162 Mon Sep 17 00:00:00 2001 From: Junichi Kato Date: Wed, 5 Aug 2026 09:56:00 +0900 Subject: [PATCH 1/6] feat(intent-completion): port the five-harness opt-in live conformance core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports Bolt 5 (#2067) from codex/intent-autonomy onto the landed runtime: the harness-neutral intent-completion Core (credential-attested authorization, reconcile-first reservation, single-use dispatch permits, canonical claims, receipt validation, all-five completion evidence, terminal plan/accept, memory ledger, U4 completed-review seed), the LiveAuthorizationPort seam on the Loop Monitor coordinator, the build-time harness registry export, and INTENT_COMPLETION_TRANSACTION_COMMITTED as canonical audit event 90 synchronized across the registry, vocabulary, docs, and drift suites. Receipt cohorts gate live verification evidence only — the Core terminal path never imports them, so workflow completion stays receipt-free by contract. t433's completion-seal fixture now rides the registry-guarded emitter instead of a raw shard row. --- .../code-generation/code-generation-plan.md | 45 + .../code-generation/code-summary.md | 74 + .../functional-design/business-logic-model.md | 570 +++++++ .../functional-design/business-rules.md | 109 ++ .../functional-design/domain-entities.md | 164 ++ .../functional-design-questions.md | 24 + .../nfr-design/logical-components.md | 43 + .../nfr-design/nfr-design-questions.md | 19 + .../nfr-design/performance-design.md | 55 + .../nfr-design/reliability-design.md | 35 + .../nfr-design/scalability-design.md | 25 + .../nfr-design/security-design.md | 27 + docs/reference/12-state-machine.md | 1 + .../knowledge/amadeus-shared/audit-format.md | 3 +- .../framework/core/otel/event-registry.ts | 20 +- .../framework/core/tools/amadeus-audit.ts | 2 + .../core/tools/amadeus-harness-registry.ts | 9 + .../tools/amadeus-intent-autonomy-runtime.ts | 4 +- .../core/tools/amadeus-intent-completion.ts | 1379 +++++++++++++++++ .../core/tools/amadeus-loop-monitor-replay.ts | 4 + .../tools/amadeus-loop-monitor-runtime.ts | 61 +- packages/framework/harness/registry.ts | 3 + tests/.coverage-ratchet.json | 2 +- tests/.coverage-registry.json | 18 +- .../integration/event-registry-drift.test.ts | 12 +- ...ntent-autonomy-runtime.integration.test.ts | 2 +- ...t433-autonomy-review-observability.test.ts | 34 +- ...ive-harness-projection.integration.test.ts | 29 + ...t-completion-live-seam.integration.test.ts | 61 + .../t48-audit-event-emitters.test.ts | 6 +- tests/no-silent-drop/baseline.json | 4 +- tests/no-silent-drop/exemptions.json | 2 +- tests/unit/t28-audit-event-sync.test.ts | 7 +- tests/unit/t434-intent-completion.test.ts | 502 ++++++ tests/unit/t81.test.ts | 5 +- 35 files changed, 3309 insertions(+), 51 deletions(-) create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/code-generation/code-generation-plan.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/code-generation/code-summary.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/business-logic-model.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/business-rules.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/domain-entities.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/functional-design-questions.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/logical-components.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/nfr-design-questions.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/performance-design.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/reliability-design.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/scalability-design.md create mode 100644 amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/security-design.md create mode 100644 packages/framework/core/tools/amadeus-intent-completion.ts create mode 100644 packages/framework/harness/registry.ts create mode 100644 tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts create mode 100644 tests/integration/t434-intent-completion-live-seam.integration.test.ts create mode 100644 tests/unit/t434-intent-completion.test.ts diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/code-generation/code-generation-plan.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/code-generation/code-generation-plan.md new file mode 100644 index 000000000..3a51c8079 --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/code-generation/code-generation-plan.md @@ -0,0 +1,45 @@ +# Code Generation Plan — five-harness-intent-completion + +## スコープと追跡元 + +U5 `five-harness-intent-completion`(Issue #2067)だけを `self-feature` として実装する。正本は U5 の Functional / NFR Design と、U1 の live authorization、U3 の Intent autonomy projection、U4 の completed review continuation である。Claude Code、Codex、Cursor、OpenCode、Kimi Code の現行5 harnessすべてについて、同一実装・package・registry・scenarioに束縛されたcredential-attested live receiptが各1件そろった場合だけ、full grant completion、workflow clear、`WORKFLOW_COMPLETED` を一つのterminal transactionで確定する。 + +Kiro / Kiro IDEを含む全7 harnessのregistryは一つのCore正本から投影する。現時点のlive cohortは現行5 harnessから導出し、将来のharness追加時にcompletion algorithmを複製しない。配布物はpackage generatorから生成し、`dist/`を直接編集しない。 + +## 実装計画 + +- [x] **Step 1 — canonical harness registry**: 全7 harnessのpackage / self-install / live-completion capabilityを一つのregistryへ集約し、現行5 cohortを導出する。 +- [x] **Step 2 — credential-attested authorization**: native environment、issuer、trace / span、attestation metadataを必須化し、欠落時は `PROVENANCE_REQUIRED` としてlive dispatchを拒否する。 +- [x] **Step 3 — single-use dispatch**: reserve、reconcile-first、canonical claim、branded permit、stale / reused permit拒否、native operation / attempt idempotencyを実装する。 +- [x] **Step 4 — receipt validation**: canonical auth / audit snapshot、Judge start / result、`AUTO_DECIDED` またはloud degradation proofを検証し、skip、forgery、revision mismatchを拒否する。 +- [x] **Step 5 — cohort evaluation**: 同一implementation / package / registry / scenarioへ束縛された各harness exactly-one receiptだけをcanonical順で受理し、missing / duplicate / mismatchをterminal evidenceにしない。 +- [x] **Step 6 — atomic completion**: completion evidence、任意の `INTENT_GRANT_COMPLETED`、`WORKFLOW_STATE_CLEARED`、`WORKFLOW_COMPLETED` を固定順序・単一revisionのterminal transactionとしてcommitする。 +- [x] **Step 7 — persistence / replay**: completion seal、event identities、projection revisionを検証し、snapshot reloadとidempotent replayを実装する。U4 completed review seedを維持する。 +- [x] **Step 8 — nonterminal absence**: credential / capability不足は `AWAITING_HUMAN` とし、成功receiptやcompletion evidenceを捏造しない。 +- [x] **Step 9 — audit / projection**: `INTENT_COMPLETION_TRANSACTION_COMMITTED` をcanonical audit eventとして登録し、全7配布treeと現行5 self-install surfaceへ投影する。 +- [x] **Step 10 — verification**: focused behavior、five-harness projection、opt-in live seam、Event Registry drift、typecheck、Biome、coverage、全integration、package / promote drift、`git diff --check`を検証する。 + +## 非目標 + +PR / GitHub / merge semantics、外部runner / supervisor、credential発行・保存、live attestationの代作、harness固有completion Core、U1〜U4のauthority再実装は対象外とする。Kiro / Kiro IDEはregistryから削除せず、live completion capabilityがfalseの間は必須cohortへ含めない。 + +## 検証基準 + +- deterministic test doubleではhappy pathとmissing / duplicate / skip / forged / mismatch / partial commitを網羅する。 +- opt-in live seamは5 harnessすべての実credential-attestationが存在する場合だけ実行し、欠落時は明示的にskipする。 +- `bun run typecheck`、対象Biome、coverage freshness、全integration、`bun scripts/package.ts --check`、`bun run promote:self:check`、`git diff --check`がgreenである。 +- live実行0件をpassとして扱わず、credential不足を非terminal blockerとして記録する。 + +## Review — Iteration 1 + +- **Verdict:** READY +- **Reviewer:** amadeus-architecture-reviewer-agent +- **Date:** 2026-08-04T01:15:20Z +- **Iteration:** 1 +- **Scope decision:** none + +U5はUnit間の直列依存を保ちつつ、5 harness native scenarioを独立実行可能にし、同一revisionへ束縛されたattested receiptを任意の適合性証拠として扱う。欠損・skip・不一致をlive passには数えない一方、Core terminal completionからは切り離す。宣言された検証証跡にも失敗はない。 + +### Findings + +- None diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/code-generation/code-summary.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/code-generation/code-summary.md new file mode 100644 index 000000000..6c287f85d --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/code-generation/code-summary.md @@ -0,0 +1,74 @@ +# Code Summary — five-harness-intent-completion + +## 実装結果 + +U5 `five-harness-intent-completion`(Issue #2067)は、現行5 harnessのcredential-attested receiptを検証するharness-neutralなopt-in適合性機構として実装した。receipt cohortの完全一致はlive verification evidenceの条件であり、CoreのIntent completion条件ではない。credentialまたはnative capabilityが不足する場合はliveを理由付きskipとし、通常のworkflow completionを妨げない。 + +## 主な変更 + +- `packages/framework/core/tools/amadeus-harness-registry.ts` と `packages/framework/harness/registry.ts` + - Claude Code、Codex、Cursor、OpenCode、Kimi Code、Kiro、Kiro IDEのcanonical registryを追加した。 + - package / self-install / live cohortを同じrowから導出し、hard-coded cohortの重複を除去した。 +- `packages/framework/core/tools/amadeus-intent-completion.ts` + - credential-attested authorization、reconcile-first reservation、single-use dispatch permit、canonical dispatch claim、native idempotency receipt検証を実装した。 + - auth / audit / Judge / automatic decision evidenceを検証し、missing、duplicate、skip、forgery、revision / binding mismatchをfail-closedにした。 + - all-five live verification evidence、検証transaction、memory ledger、snapshot reload、idempotent replay、U4 completed review seedを実装した。Core terminal pathはこのevidenceをimportしない。 +- `packages/framework/core/tools/amadeus-loop-monitor-runtime.ts`、`amadeus-intent-autonomy.ts`、`amadeus-intent-autonomy-runtime.ts` + - live authorization metadataを後方互換で拡張し、terminal projectionとしてworkflow / current grantのclearを合法化した。 + - terminal live completion capabilityを有効化した。 +- `packages/framework/core/tools/amadeus-audit.ts`、`packages/framework/core/otel/event-registry.ts`、`packages/framework/core/knowledge/amadeus-shared/audit-format.md`、`docs/reference/12-state-machine.md` + - `INTENT_COMPLETION_TRANSACTION_COMMITTED` をcanonical 86件目のaudit eventとして同期した。 + - typed repository event constructionもemitter drift guardが認識するようにし、U4 / U5のtaxonomy追従を検証した。 +- `tests/unit/t434-intent-completion.test.ts` + - registry、authorization absence、single-use dispatch、all-five happy path、terminal atomicity / replay、U4 continuation、missing / duplicate / skip / forged / mismatch / partial transaction拒否を検証した。 +- `tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts` + - 現行5 harnessの配布物が同一registry / completion Core bytesを持つことを検証した。 +- `tests/integration/t434-intent-completion-live-seam.integration.test.ts` + - 5 harnessすべての明示的live attestationが存在する場合だけ動くopt-in seamを追加した。 +- package generatorにより全7配布treeを同期し、現行5 self-install harnessへ共通Coreをpromoteした。 + +## 安全境界 + +- credential / attestation metadataがないauthorizationは `PROVENANCE_REQUIRED` で拒否する。 +- receiptはimplementation revision、package digest、registry revision、scenario revision、Intent / grant、native operation / attemptへ束縛する。 +- 5 harnessのどれか1件でも欠落・重複・不一致ならlive verification transactionを開始しない。 +- live verification transactionは部分receiptを成功として返さず、event identitiesとprojection revisionの完全一致を要求する。 +- PR / GitHub / merge、外部runner / supervisor、credential管理はCoreへ含めていない。 + +## 検証結果 + +- focused U5 unit: 6 tests / 33 expects、全件pass。 +- five-harness projection: 5 tests / 10 expects、全件pass。 +- U4 / U5 focused suite: 15 pass / 1 live skip、失敗0。 +- Event Registry / coverage focused suite: 58 tests / 1,832 expects、全件pass。 +- `bun run typecheck`: pass。 +- 対象Biome check: warning / errorともに0。 +- `bun run lint`: exit 0。repository既存baselineの398 warnings / 23 infosがあり、新規U5 fileのwarningは0。 +- integration verbose再実行: 404 test files / 4,787 assertions、失敗0。 +- 最終focused regression: 52 pass / 1 live skip / 0 fail、632 expects。 +- `bun tests/gen-coverage-registry.ts --check`: pass。 +- `bun scripts/package.ts --check`: 全7生成treeでpass。 +- `bun run promote:self:check`: 現行5 self-install harnessでpass。 +- `git diff --check`: pass。 + +## 全体CIと再実行 + +初回 `bun run test:ci` は768 test filesを完走し、4 files / 5 assertionsが失敗した。確認できた決定的失敗は、U4 / U5で追加された2監査イベントが `docs/reference/12-state-machine.md` とtyped repository emitter検出へ未反映だった `t48-audit-event-emitters` と、そのmeta-testである `t52-drift-meta-validation` だった。taxonomyとdrift guardを修正後、両ファイルは22 tests / 41 expectsで全件passした。 + +初回出力で名称を保持できなかった残り2ファイルは、全integrationをverboseで再実行しても再現せず、404 / 404 filesがpassした。重い既知suiteもtimeoutせずpassしており、U5回帰は確認されなかった。AWS credentialsは無効または期限切れのため、runner規則によりlive SDK / substrate testはskipされた。 + +## Live verification + +実live verificationは0件である。`AMADEUS_INTENT_COMPLETION_LIVE=1` とnative command設定がそろっていないため、U5 live seamは1件skipした。 + +- `AMADEUS_CLAUDE_LIVE_ATTESTATION` +- `AMADEUS_CODEX_LIVE_ATTESTATION` +- `AMADEUS_CURSOR_LIVE_ATTESTATION` +- `AMADEUS_OPENCODE_LIVE_ATTESTATION` +- `AMADEUS_KIMI_LIVE_ATTESTATION` + +このskipはlive passではないが、Core Intent completionのblockerでもない。test double以外のreceiptやattestationは生成していない。 + +## 残作業 + +実credential-attestationを利用したall-five live receiptの取得は環境依存の任意検証として残る。Core Intent completionとは独立して後から実行できる。 diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/business-logic-model.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/business-logic-model.md new file mode 100644 index 000000000..8eb3450eb --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/business-logic-model.md @@ -0,0 +1,570 @@ +# Business Logic Model — five-harness-intent-completion + +## 上流入力と設計範囲 + +本設計は`units-generation/unit-of-work.md`、`units-generation/unit-of-work-story-map.md`、`requirements-analysis/requirements.md`、`application-design/components.md`、`application-design/component-methods.md`、`application-design/services.md`を正本とする。対象はU5 `five-harness-intent-completion`、FR-HAR-001〜007、2067-AC22〜26、およびU5へ割り当てられたterminal persistence / privacy / drift contractである。 + +U1のgeneric `LiveAuthorizationPort`とprotected authorization eventを再利用し、M08 receipt validation / cohort evaluation、M09 native live scenario、M06 / M04 / M07 terminal transactionを閉じる。PR / merge、外部runner、Kiro / Kiro IDE live対応、credential保存、harness固有Core algorithmは所有しない。 + +## Closed public contract + +`StableId`、`Sha256Digest`、`AuditEventPlan`、`CanonicalAuditEvent`、`AuditCommitReceipt`、`AuditTransaction`、`ContractResult`、`HarnessDescriptor`、`ValidatedHarnessRegistry`、`RevisionBinding`、`LiveAuthorizationPort`、`LiveExecutionAuthorization`、`WorkflowResult`、`AutonomyProjection`は上流M00 / M04 / M06 / M08の定義を使う。 + +```ts +type CompletionHarnessId = HarnessDescriptor["id"]; +type RequiredCompletionCohort = { + schemaVersion: "1"; + cohortId: "intent-autonomy-live"; + harnessIds: readonly CompletionHarnessId[]; + registryDigest: Sha256Digest; + cohortDigest: Sha256Digest; +}; +type LiveScenarioRevision = RevisionBinding & { + registryDigest: Sha256Digest; + scenarioDigest: Sha256Digest; +}; +type IntentLiveExecutionAuthorization = Omit & { + schemaVersion: "1"; + cohortDigest: Sha256Digest; + revision: LiveScenarioRevision; + authorizationPayloadDigest: Sha256Digest; +}; +type IntentLiveAuthorizationEventPlan = AuditEventPlan & { + eventType: "LIVE_SMOKE_AUTHORIZED"; + eventIdentity: StableId; + fields: { payload_v1: string }; +}; +type CommittedIntentLiveExecutionAuthorization = IntentLiveExecutionAuthorization & { + commitReceipt: AuditCommitReceipt; +}; +type LiveObservation = { + judgeInvocationId: StableId; + judgeObserved: true; + electionDecisionId: StableId; + electionOutcome: "elected" | "loud-degradation"; + degradationReason: StableId | null; +}; +type IntentLiveRunReservation = { + schemaVersion: "1"; + runId: StableId; + operationReference: StableId; + intentUuid: StableId; + harnessId: CompletionHarnessId; + authorizationId: StableId; + judgeInvocationId: StableId; + revision: LiveScenarioRevision; + maxDispatches: 2; + dispatchesAuthorized: 1 | 2; + status: + | "reserved" + | "started" + | "redispatch-authorized" + | "dispatch-claimed" + | "completed" + | "incomplete"; +}; +type IntentLiveRunStateEventPlan = AuditEventPlan & { + eventType: + | "LIVE_SMOKE_RUN_RESERVED" + | "LIVE_SMOKE_RUN_STARTED" + | "LIVE_SMOKE_RUN_REDISPATCH_AUTHORIZED" + | "LIVE_SMOKE_RUN_DISPATCH_CLAIMED" + | "LIVE_SMOKE_RUN_COMPLETED" + | "LIVE_SMOKE_RUN_INCOMPLETE"; + eventIdentity: StableId; + fields: { payload_v1: string }; +}; +type NativeRunReconciliation = + | { kind: "completed"; receipt: RawIntentLiveReceipt; proofDigest: Sha256Digest } + | { kind: "attested-no-effect"; proofDigest: Sha256Digest } + | { kind: "effect-possible" | "unknown"; proofDigest: Sha256Digest | null }; +type CommittedIntentLiveRunState = IntentLiveRunReservation & { + stateEventIdentity: StableId; + stateCommitReceipt: AuditCommitReceipt; + sourceAuditRevision: number; + sourceStateProjectionRevision: number; +}; +type CanonicalLiveRunSnapshot = { + intentUuid: StableId; + auditRevision: number; + stateProjectionRevision: number; + authorization: CommittedIntentLiveExecutionAuthorization; + run: CommittedIntentLiveRunState; +}; +type CanonicalLiveRunStateReader = { + readRunSnapshot(input: { + intentUuid: StableId; + runId: StableId; + }): ContractResult; +}; +type AttestedNoEffectProofVerifier = { + verify(input: { + snapshot: CanonicalLiveRunSnapshot; + reconciliation: Extract; + }): ContractResult<{ proofDigest: Sha256Digest }>; +}; +declare const runDispatchPermitBrand: unique symbol; +type RunDispatchPermit = { + schemaVersion: "1"; + permitId: StableId; + runId: StableId; + authorizationId: StableId; + operationReference: StableId; + judgeInvocationId: StableId; + attempt: 1 | 2; + authorizedStateEventIdentity: StableId; + authorizedCommitTransactionId: StableId; + sourceAuditRevision: number; + permitDigest: Sha256Digest; + readonly [runDispatchPermitBrand]: true; +}; +declare const claimedRunDispatchBrand: unique symbol; +type ClaimedRunDispatch = RunDispatchPermit & { + claimEventIdentity: StableId; + claimCommitTransactionId: StableId; + claimAuditRevision: number; + readonly [claimedRunDispatchBrand]: true; +}; +type NativeDispatchReceipt = { + schemaVersion: "1"; + dispatchKeyDigest: Sha256Digest; + nativeOperationId: StableId; + outcome: "started" | "attached"; + proofDigest: Sha256Digest; +}; +type RawIntentLiveReceipt = { + schemaVersion: "1"; + receiptId: StableId; + intentUuid: StableId; + harnessId: CompletionHarnessId; + authorizationId: StableId; + authorizationEventIdentity: StableId; + authorizationCommitTransactionId: StableId; + revision: LiveScenarioRevision; + environmentId: StableId; + traceId: StableId; + spanId: StableId; + attestationDigest: Sha256Digest; + outcome: "passed" | "skipped" | "failed"; + observation: LiveObservation | null; +}; +type CanonicalLiveAuthorizationSnapshot = { + intentUuid: StableId; + audit: readonly CanonicalAuditEvent[]; + auditRevision: number; + stateProjectionRevision: number; + authorization: CommittedIntentLiveExecutionAuthorization; +}; +type CanonicalLiveEvidenceReader = { + readAuthorizationSnapshot(input: { + intentUuid: StableId; + authorizationId: StableId; + }): ContractResult; +}; +type ValidatedIntentLiveReceipt = RawIntentLiveReceipt & { + outcome: "passed"; + observation: LiveObservation; + observationProofDigest: Sha256Digest; + validationDigest: Sha256Digest; +}; +type LiveReceiptValidatedEventPlan = AuditEventPlan & { + eventType: "LIVE_SMOKE_RECEIPT_VALIDATED"; + eventIdentity: StableId; + fields: { payload_v1: string }; +}; +type PlannedValidatedIntentLiveReceipt = { + receipt: ValidatedIntentLiveReceipt; + audit: readonly [LiveReceiptValidatedEventPlan]; +}; +type CommittedValidatedIntentLiveReceipt = ValidatedIntentLiveReceipt & { + validationEventIdentity: StableId; + validationCommitReceipt: AuditCommitReceipt; +}; +type CanonicalValidatedReceiptSetSnapshot = { + intentUuid: StableId; + auditRevision: number; + stateProjectionRevision: number; + receipts: readonly CommittedValidatedIntentLiveReceipt[]; +}; +type CanonicalValidatedReceiptReader = { + readValidationSet(input: { + intentUuid: StableId; + validationEventIdentities: readonly StableId[]; + }): ContractResult; +}; +type IntentCompletionEvidence = { + schemaVersion: "1"; + evidenceId: StableId; + intentUuid: StableId; + cohort: RequiredCompletionCohort; + revision: LiveScenarioRevision; + receiptIds: readonly StableId[]; + authorizationIds: readonly StableId[]; + validationEventIdentities: readonly StableId[]; + validationDigests: readonly Sha256Digest[]; + observationProofDigests: readonly Sha256Digest[]; + sourceAuditRevisions: readonly number[]; + evidenceDigest: Sha256Digest; +}; +type CompletionEvidenceValidatedPayloadV1 = { + schemaVersion: "1"; + evidenceId: StableId; + intentUuid: StableId; + cohortId: RequiredCompletionCohort["cohortId"]; + cohortDigest: Sha256Digest; + implementationRevision: StableId; + packageDigest: Sha256Digest; + registryDigest: Sha256Digest; + scenarioDigest: Sha256Digest; + harnessIds: readonly CompletionHarnessId[]; + receiptIds: readonly StableId[]; + authorizationIds: readonly StableId[]; + validationEventIdentities: readonly StableId[]; + validationDigests: readonly Sha256Digest[]; + observationProofDigests: readonly Sha256Digest[]; + sourceAuditRevisions: readonly number[]; + evidenceDigest: Sha256Digest; + sourceAuditRevision: number; +}; +type CompletionEvidenceValidatedEventPlan = AuditEventPlan & { + eventType: "LIVE_COMPLETION_EVIDENCE_VALIDATED"; + eventIdentity: StableId; + intentUuid: StableId; + fields: { payload_v1: string }; + payloadDigest: Sha256Digest; +}; +type IntentCompletionCheck = + | { kind: "complete"; evidence: IntentCompletionEvidence } + | { + kind: "incomplete"; + intentUuid: StableId; + cohort: RequiredCompletionCohort; + revision: LiveScenarioRevision; + missingHarnessIds: readonly CompletionHarnessId[]; + rejectedReceiptIds: readonly StableId[]; + }; +type IntentCompletionEvaluation = + | { + check: Extract; + audit: readonly [CompletionEvidenceValidatedEventPlan]; + sourceAuditRevision: number; + sourceStateProjectionRevision: number; + } + | { + check: Extract; + audit: readonly []; + sourceAuditRevision: number; + sourceStateProjectionRevision: number; + }; +type TerminalCommitPlan = { + transaction: AuditTransaction; + orderedEvents: readonly AuditEventPlan[]; + completionEvidence: IntentCompletionEvidence; + expectedEventIdentities: readonly StableId[]; + expectedStateProjectionRevision: number; +}; +type TerminalCommitReceipt = { + evidenceId: StableId; + transactionId: StableId; + committedEventIdentities: readonly StableId[]; + stateProjectionRevision: number; + result: WorkflowResult & { outcome: "completed" }; +}; + +type IntentLiveReceiptValidator = { + validate(receipt: RawIntentLiveReceipt): ContractResult; + bindCommit(input: { + planned: PlannedValidatedIntentLiveReceipt; + receipt: AuditCommitReceipt; + }): ContractResult; +}; +type IntentLiveAuthorizationService = { + authorize(input: { + intentUuid: StableId; + harnessId: CompletionHarnessId; + revision: LiveScenarioRevision; + cohort: RequiredCompletionCohort; + registry: ValidatedHarnessRegistry; + }): Promise>; + bindCommit(input: { + authorization: IntentLiveExecutionAuthorization; + audit: readonly [IntentLiveAuthorizationEventPlan]; + receipt: AuditCommitReceipt; + }): ContractResult; +}; +function resolveRequiredCompletionCohort( + registry: ValidatedHarnessRegistry, +): ContractResult; +function createIntentLiveReceiptValidator(deps: { + evidenceReader: CanonicalLiveEvidenceReader; +}): IntentLiveReceiptValidator; +function createIntentLiveAuthorizationService(deps: { + port: LiveAuthorizationPort; +}): IntentLiveAuthorizationService; +type IntentCompletionEvaluator = { + evaluate(input: { + intentUuid: StableId; + cohort: RequiredCompletionCohort; + revision: LiveScenarioRevision; + validationEventIdentities: readonly StableId[]; + }): ContractResult; +}; +type IntentLiveRunCoordinator = { + reserve(input: { + authorization: CommittedIntentLiveExecutionAuthorization; + }): ContractResult<{ + reservation: IntentLiveRunReservation; + audit: readonly [IntentLiveRunStateEventPlan]; + }>; + bindStateCommit(input: { + planned: IntentLiveRunReservation; + audit: readonly [IntentLiveRunStateEventPlan]; + receipt: AuditCommitReceipt; + }): ContractResult; + planNext(input: { + intentUuid: StableId; + runId: StableId; + }): Promise>; + bindTransitionCommit(input: { + prior: CommittedIntentLiveRunState; + next: IntentLiveRunReservation; + audit: readonly [IntentLiveRunStateEventPlan]; + receipt: AuditCommitReceipt; + }): ContractResult<{ + state: CommittedIntentLiveRunState; + dispatchPermit: RunDispatchPermit | null; + }>; + claimDispatch(permit: RunDispatchPermit): ContractResult<{ + prior: CommittedIntentLiveRunState; + claimed: IntentLiveRunReservation; + audit: readonly [IntentLiveRunStateEventPlan]; + }>; + bindDispatchClaimCommit(input: { + permit: RunDispatchPermit; + prior: CommittedIntentLiveRunState; + claimed: IntentLiveRunReservation; + audit: readonly [IntentLiveRunStateEventPlan]; + receipt: AuditCommitReceipt; + }): ContractResult; + dispatch(claimed: ClaimedRunDispatch): Promise>; +}; +type IntentNativeRunPort = { + reconcile(input: { + operationReference: StableId; + judgeInvocationId: StableId; + }): Promise>; + dispatch(claimed: ClaimedRunDispatch): Promise>; +}; +function createIntentCompletionEvaluator(deps: { + receiptReader: CanonicalValidatedReceiptReader; +}): IntentCompletionEvaluator; +function createIntentLiveRunCoordinator(deps: { + runReader: CanonicalLiveRunStateReader; + proofVerifier: AttestedNoEffectProofVerifier; + nativePort: IntentNativeRunPort; +}): IntentLiveRunCoordinator; +function planTerminalCommit(input: { + current: AutonomyProjection; + evaluation: Extract; +}): ContractResult; +function acceptTerminalCommit(input: { + plan: TerminalCommitPlan; + receipt: AuditCommitReceipt; +}): ContractResult; +``` + +## 1. Completion cohort resolution + +M08 registryを唯一のauthoring sourceとする。cohort resolverはdescriptor順を使わずharness ID昇順へ正規化し、次をすべて満たすrowだけを`intent-autonomy-live` cohortへ含める。 + +- `packageFace=true` +- `autonomyContract=true` +- `autonomyLive=true` +- `native.liveAuthorization=credential-attested` +- `native.judgeReplay=invoke-once` + +GA registryでは結果がexactly `claude / codex / cursor / opencode / kimi`であることをdrift fixtureが検証する。Kiro / Kiro IDEはfalseのため除外する。`CompletionHarnessId`は手書きunionではなくregistryから生成される`HarnessDescriptor["id"]`を参照し、membershipはcohortが決める。Core evaluatorは5という数やharness名を分岐へ埋め込まず、validated cohortの全memberを要求する。将来harnessはregistry rowとnative adapterを追加して同cohortへ参加でき、M04〜M07のalgorithm変更や手書きcontract union編集を要求しない。schema v1の現行contract fixtureだけが上記5件をexact oracleとして固定する。 + +cohort digestは`amadeus.completion-cohort.v1` domainでschema / cohort ID / registry digest / sorted harness IDsから作る。empty、duplicate、unknown ID、capability不一致を`MALFORMED`で拒否する。 + +## 2. Credential-attested authorization + +各harnessのlive runは次の順序だけを許可する。 + +1. M06がtarget Intentと`LiveScenarioRevision`を固定し、U5-owned `IntentLiveAuthorizationService.authorize`を呼ぶ。 +2. serviceがU1の`LiveAuthorizationPort.authorize`へbase `RevisionBinding`を渡し、credential-attested environment adapterからsafe environment ID、issuer principal、trace / span、attestation digestを得る。credential値は返さない。 +3. serviceがvalidated registry / cohortを照合し、base authorizationをregistry / scenario digestまで含むclosed `IntentLiveExecutionAuthorization`へ拡張する。型宣言順fixed-key JSONをexactly one `payload_v1` fieldへ保存した`LIVE_SMOKE_AUTHORIZED` eventを計画する。 +4. M07がprotected appendし、`bindCommit`がcommit receipt内のtransaction / authorization event identityを確認した場合だけ`CommittedIntentLiveExecutionAuthorization`を作る。 +5. M09はそのcommitted authorizationを受けた場合だけnative scenarioを開始する。 + +authorization payload digestはIntent、harness、cohort、implementation / package / registry / scenario、environment、trace / span、attestationへ束縛する。authorization IDは`amadeus.live-authorization.v1`、event IDは`amadeus.live-authorization-event.v1`のcanonical tupleから作る。再送は同じcommit receiptを返し、異なるenvironment / revision / scenario / attestationで同じIDを使えない。 + +## 3. Native live scenario + +M09 scenarioは認可された一時workspaceだけで、不可逆な外部effectを持たない。全harnessで同じscenario digestとassertion setを使い、native差はadapterがcommand / hook / skill surfaceへ写像する。 + +1. M06はcommitted authorizationごとに`amadeus.intent-live-run.v1`からrun ID、operation reference、Judge invocation IDを決定し、`LIVE_SMOKE_RUN_RESERVED`をprotected appendする。`bindStateCommit`がevent containment、transaction ID、projection revisionを検証して`CommittedIntentLiveRunState`を返すまで次の操作を許可しない。 +2. 初回またはresumeのcoordinatorはcallerからrun state / reconciliationを受けず、`CanonicalLiveRunStateReader`でcurrent committed runを再読してから`IntentNativeRunPort.reconcile`を同じoperation reference / Judge invocation IDで呼ぶ。`completed`ならdispatchせずreceiptを採用する。 +3. reservation直後の`attested-no-effect`は`AttestedNoEffectProofVerifier`がcanonical run / authorization / environment operation logへexact matchした場合だけ受理する。coordinatorは`LIVE_SMOKE_RUN_STARTED(attempt=1)`を計画し、M07 commit後の`bindTransitionCommit`だけがbranded `RunDispatchPermit`を発行する。permitはnative effectを直接許可せず、`claimDispatch`がcurrent headへ`LIVE_SMOKE_RUN_DISPATCH_CLAIMED`をCAS appendし、`bindDispatchClaimCommit`がcommit receiptを検証して返す`ClaimedRunDispatch`だけがnative portを呼べる。attempt 1 claim後も同じ検証済みno-effectに限り`LIVE_SMOKE_RUN_REDISPATCH_AUTHORIZED(attempt=2)`を計画し、同じclaim手順で最大1回再dispatchする。 +4. `effect-possible / unknown`、またはattempt 2後もreceiptを回収できない場合は`LIVE_SMOKE_RUN_INCOMPLETE`をcommitし、以後dispatchしない。このrunはfailedとしてcohortをincompleteに保つ。 +5. 対象revisionのpackageを一時workspaceへinstallし、synthetic workflowを起動する。Judgeはstable invocation IDのpending eventから`invokeOnce`され、adapterは同じoperationへのattach / reconcileを別runとして扱わない。 +6. 自動裁定がsolo electionを使用できる場合はelection decisionを観測する。native electionが使えない場合はrecommendationへのloud degradation eventとreasonを観測する。 +7. workflowが期待resultへ到達し、audit / status / replayが同じdecision、latch、grant、queueを返すことを確認する。safe raw receiptとreconciliation proofを`LIVE_SMOKE_RUN_COMPLETED`へcommitした後、M08 validationへ渡す。 + +`judgeObserved=true`はJudge request / result event identityとtraceが一致した場合だけ、`electionOutcome`はcanonical decision / degradation eventが同じtraceに存在する場合だけ設定する。adapterの自己申告booleanをvalidatorが信用しない。 + +run state eventはexactly one `payload_v1` fieldを持ち、Intent、harness、authorization、revision、run / operation / invocation、attempt budget、status、nullable reconciliation proof digestをclosed型宣言順JSONで保存する。run event identityは`amadeus.intent-live-run-event.v1`でrun ID、status、dispatches authorized、previous event identity、payload digestへ束縛する。M07はdense state transitionと同一runのfork不在を検証し、unknown field、budget超過、別operation / invocationを拒否する。 + +dispatch permitは`amadeus.intent-live-run-dispatch-permit.v1`でrun / authorization / operation / invocation / attempt、authorized state event、commit transaction、source audit revisionへ束縛する。`bindTransitionCommit`はcommit receiptを検証した後、canonical current stateが同じauthorized eventである場合だけpermitを生成する。`dispatch`は直前にrun readerを再読してpermit digest、current state、attempt budgetを再検証し、stale / forged / reused permitを拒否してからnative portへ渡す。native portはplain reservation、authorization、operation IDを受けるoverloadを持たない。 + +dispatch claim eventはpermit ID、operation reference、attempt、authorized state eventを`amadeus.intent-live-run-dispatch-claim.v1`へ束縛し、M07がcurrent headとattempt未claimを同じlockでCASする。競合claimはeventをappendせず`CONFLICT(dispatchClaim)`となり、callerはcanonical runをreconcileする。`ClaimedRunDispatch`はclaim event / commit transaction / audit revisionを含み、claim commit前には生成できない。 + +native portは`dispatchKeyDigest = sha256(amadeus.intent-live-native-dispatch.v1, operationReference, attempt)`をauthoritative idempotency keyとして、同じkeyの並行・再送をexactly one native operationへ線形化する。winnerは`outcome=started`、競合callerは同じ`nativeOperationId` / proof digestを持つ`outcome=attached` receiptを得て新しいJudgeを起動しない。coordinatorはdispatch直前にclaim headを再読し、native receiptのkey / operation / proofを検証する。これにより同じclaimed tokenが再構築されてもnative effectは増えず、以後は同じoperationをreconcileする。 + +## 4. Receipt validation + +M08-owned validatorはcaller提供auditやauthorization commit receiptを受け取らない。M08-owned `CanonicalLiveEvidenceReader` portをM07 adapterが実装し、closed U5 authorization payload、commit receipt、canonical audit、authoritative audit revision、state projection revisionを同一snapshotで返す。 + +validationは次をexact matchする。 + +- target Intent / harness / authorization ID / event identity +- implementation revision / package / registry / scenario digest +- environment / trace / span / attestation digest +- authorization commit transactionとevent containment +- registry cohort membershipとnative capability +- `outcome=passed` +- Judge request / resultの同一invocation・trace観測 +- electionまたはloud degradationのdecision・trace観測 + +`skipped / failed`、null observation、duplicate event、unknown field、redaction failure、canonical source mismatchを拒否する。validatorはJudge request / resultとelection / degradation proofのcanonical digestを`observationProofDigest`へまとめる。validation digestは`amadeus.validated-live-receipt.v1`でraw receipt digest、authorization payload / event identity、observation proof、source audit revisionへ束縛する。 + +validation成功はまだterminal evidenceではない。validatorはclosed validation payloadをexactly one `payload_v1` fieldに持つprotected `LIVE_SMOKE_RECEIPT_VALIDATED` eventを計画する。payloadは`ValidatedIntentLiveReceipt`の全closed field(Intent、harness、receipt / authorization / authorization event / transaction、完全revision、environment / trace / span / attestation、passed outcome、Judge / election observation)、validation digest、observation proof digest、authorization source revisionを持つ。M07 commit後、`bindCommit`がtransactionとvalidation event identityを確認した場合だけ`CommittedValidatedIntentLiveReceipt`になる。 + +## 5. Five-harness evaluation + +evaluatorはcaller提供validated receipt配列を受け取らない。validation event identitiesだけを受け、M08-owned `CanonicalValidatedReceiptReader` portのM07 adapterから、protected validation events / commit receipts / canonical audit / authoritative revisionを同一snapshotで読む。closed payloadとcommit containmentを再検証し、全receiptが同じIntent / cohort / revisionを持つこと、harness IDごとexactly oneであることを確認する。receipt入力順を無視し、cohort順へ正規化する。cohort外、duplicate、missing、別revisionはcompleteへ寄与しない。 + +全memberが揃った場合だけ`IntentCompletionEvidence`とclosed `CompletionEvidenceValidatedEventPlan`を作る。evidence IDは`amadeus.intent-completion-evidence.v1`でIntent、cohort digest、revision、sorted receipt / authorization IDs、validation event IDs / digests、observation proof digests、source audit revisionsへ束縛する。evaluationはreader snapshotのauthoritative audit revisionとstate projection revisionを`sourceAuditRevision / sourceStateProjectionRevision`として返す。不足時も同じsnapshot revisions、`incomplete`、empty audit tuple、missing / rejected集合を返し、success eventを作らない。 + +event payloadは`CompletionEvidenceValidatedPayloadV1`の型宣言順fixed-key JSONであり、`AuditEventPlan.fields`はexactly one key `payload_v1`だけを持つ。全nullableなし、unknown / missing keyを拒否し、各配列はcohort順かつ長さ=cohort member数とする。payload digestは`canonical-value-v1(completion-evidence-validated-payload)`で再計算する。event identityは`amadeus.completion-evidence-validated-event.v1` tupleのIntent、evidence ID / digest、cohort digest、source audit revision、payload digestから作る。 + +M07 parserはlock内でpayloadをclosed decodeし、registry / scenarioを含むrevision、validation event IDs / digests、observation proof digests、source revisions、evidence / payload digest、event identityをcanonical validation eventsから再計算する。一つでも不一致ならterminal transaction全体を拒否する。このevent identityが`TerminalCommitPlan.expectedEventIdentities`の先頭となる。 + +## 6. Atomic terminal transition + +M06はcomplete evaluationだけを`planTerminalCommit`へ渡す。M04 `planIntentCompletion`とM06 completion planを組み合わせ、event順を次に固定する。 + +1. `LIVE_COMPLETION_EVIDENCE_VALIDATED` +2. full modeならactive grantをcompletedへするM04 event。none / semiではgrant eventなし。 +3. workflow execution stateをnullへするM04 event +4. `WORKFLOW_COMPLETED` + +transactionは`evaluation.sourceAuditRevision`をそのまま`expectedRevision`に持ち、caller指定revisionを受けない。全event planを確定した後、transaction IDを`amadeus.intent-terminal-transaction.v1` canonical tupleのIntent、completion evidence ID / digest、cohort digest、順序付きevent identities、expected audit revision、source state projection revisionから決定する。random ID、時刻、event count、先頭event IDの流用を禁止する。 + +M07は1 lock内でtransaction IDを同じtupleから再計算し、validation eventsが同じcanonical auditに存在すること、evidence digest、current mode / grant / workflow、event identity重複、expected revisionを再検証して、全件appendまたは全件拒否する。CAS成功はevaluatorが読んだvalidation snapshotから1 eventも変わっていないことを保証する。M07のstate projection revisionはevent件数ではなく成功したaudit transactionごとにexactly 1進むため、planは`expectedStateProjectionRevision = evaluation.sourceStateProjectionRevision + 1`を固定する。overflow / non-safe integerを拒否する。artifact digest / completion sealは同transaction結果から作る。 + +`acceptTerminalCommit`はplanからtransaction IDを再計算し、commit receiptのtransaction ID、expected event identities、`expectedStateProjectionRevision`をexact matchして、全eventがcommitされた場合だけcompleted resultを返す。部分commit、未知event、missing grant event、receipt mismatchではcompletedを返さない。同じplan / transaction IDのreplayはM07 idempotency indexから同じcommit receiptとterminal receiptを返し、revisionを再度進めない。 + +## 7. Persistenceとcompleted review + +canonical auditはauthorization、raw receipt validation evidence、completion evidence、terminal transactionを復元できる。session / process / compaction / clone後に次を同値比較する。 + +- cohort / revision / receipt / authorization集合 +- grant completedまたはnull +- workflow execution state=null +- completion evidence / seal / result identity +- U4 decision queue / review extension head + +runtime scratchや一時workspaceが消えてもterminal stateは変わらない。completed後の`AUTO_DECISION_REVIEWED`はU4の専用post-seal extension chainへ追記できるが、U5 completion evidence、seal、grant、workflow resultを変更しない。 + +## 8. Harness-neutral packaging + +`packages/framework/harness/registry.ts`を単一authoring sourceとし、package/setup/promote/self-install projection、`HarnessDescriptor["id"]` union、contract adapter unionをpackage生成時に再生成する。Application Designに記載した7件unionはこのcompile projectionのschema例であり、U5実装では手編集する正本にしない。future harness追加はregistry rowとnative adapterを追加し、`bun scripts/package.ts`で生成型 / distributionを更新する。drift guardは生成差分を拒否する。harness adapterはnative invocation / observation抽出だけを所有し、authorization、receipt validation、cohort evaluation、terminal algorithmを複製しない。 + +contract fixtureは現行5harnessのexact tuple、同じcanonical byte vectors、pass / skip / mismatch / forged authorization / replay casesを持つ。opt-in liveはcredentialがなければ理由付きskipとして報告するが、skipをCI pass evidenceやIntent completion evidenceへ変換しない。 + +## 要件追跡 + +| Design | Requirement / AC | +|---|---| +| cohort / registry / packaging | FR-HAR-001、005〜007、2067-AC22、26 | +| authorization / live scenario | FR-HAR-003、2067-AC23〜24 | +| receipt validation / exact five | FR-HAR-002〜004、2067-AC22〜25 | +| terminal transaction / persistence | FR-AUT-006、FR-GRT-009、FR-STP-007、NFR-DET-002、NFR-REL-003 | +| privacy / completed review | NFR-PRV-001〜002、FR-OBS-004 | + +## 非目標 + +- PR / merge / GitHub reviewをcompletionへ入力すること。 +- Kiro / Kiro IDEを今回のlive cohortへ追加すること。 +- credential / token / raw provider payloadを保存すること。 +- 常駐runner / supervisor /新stageを追加すること。 +- harness別にCore algorithmをforkすること。 + + +## Historical Review Cycle 1 — Iteration 1 + +- **Verdict:** NOT-READY +- **Reviewer:** amadeus-architecture-reviewer-agent +- **Date:** 2026-08-03T14:53:26Z +- **Iteration:** 1 +- **Scope decision:** none + +cohort評価とterminal event順は明確だが、U5固有revisionをauthorizationへ束縛する経路と、validated receiptをcanonical evidenceとして永続化・lock内再検証する経路が閉じていない。 + +### Findings + +- BLOCKER | U5はauthorizationをimplementation/package/registry/scenarioへ束縛すると規定するが、再利用する上流`LiveAuthorizationPort`の入力と`LiveExecutionAuthorization.revision`は`RevisionBinding(implementationRevision, packageDigest)`だけであり、`CommittedLiveExecutionAuthorization`にも`registryDigest / scenarioDigest`がない。U5には`LiveScenarioRevision`全体を受けてprotected authorization eventへ保存するrefined planner/APIやclosed event schemaもないため、M08 validatorはraw receiptのregistry/scenarioをcanonical authorizationとexact matchできず、同じimplementation/package上の別scenarioを区別できない。U1 authorizationを安全に拡張するU5-owned authorization refinementと、その全revision fieldを保持するcommitted snapshotを定義する必要がある。 +- BLOCKER | `ValidatedIntentLiveReceipt`はvalidatorが返すtransientな構造型だが、per-harness validation結果をcanonical auditへappendするevent/APIがなく、`evaluateIntentCompletion`はcanonical evidence readerを持たずに同型の配列を信用する。さらに`IntentCompletionEvidence`はreceipt/authorization IDとsource revisionだけで、validation digest、passed observation、Judge/election proofを保持しない。このためprocess/clone再開時にvalidated receipt集合をauditから復元できず、M07 terminal lock内でも5件のpassed・authorization・observationを再検証できないため、CASはstate競合だけを防いで偽造またはstale evidenceによる完了を防げない。各receiptのclosed validation eventをprotected appendしてevidenceへdigestを束縛するか、evaluator/terminal validatorがraw receiptとcanonical snapshotをlock内で再検証する必要がある。 +- FOLLOW-UP | `CompletionHarnessId`が現行5件のclosed unionで、cohort・raw receiptもこの型へ固定されているため、future harness参加にはregistry rowとadapter以外にCore contract型の編集が必要になる。FR-HAR-006はalgorithm fork禁止としては満たせるが、「registry rowとnative adapter追加だけ」という設計主張を厳密に保つなら、registry生成型へ寄せるか現行5件unionが生成物であることを明記するとよい。 + +## Historical Review Cycle 1 — Iteration 2 + +- **Verdict:** NOT-READY +- **Reviewer:** amadeus-architecture-reviewer-agent +- **Date:** 2026-08-03T14:58:20Z +- **Iteration:** 2 +- **Scope decision:** none + +前回2 BLOCKERは解消された。full revision authorizationとper-harness validation persistence/evaluationは接続済みだが、terminal lock内検証の正本となるcompletion evidence eventのwire・identity契約が未定義である。 + +### Findings + +- BLOCKER | `LIVE_COMPLETION_EVIDENCE_VALIDATED`はterminal transactionの先頭eventであり、M07がlock内でvalidation event集合とevidence digestを再検証する正本だが、対応するclosed EventPlan型、`payload_v1` schema、canonical serialization、event identity生成規則がない。`IntentCompletionEvaluation.audit`は汎用`AuditEventPlan[]`に留まり、authorization/receipt validation eventで定義されたexactly-one-field規則も適用されていない。このままではM06/M07がevidenceの全field、validation IDs/digests、observation proof、source revisionsを同じwire表現へ実装できず、replay時のevent identityと`acceptTerminalCommit.expectedEventIdentities`も一意にならない。`CompletionEvidenceValidatedEventPlan`相当のclosed schema、canonical payload/event identity、M07 parser検証規則を定義する必要がある。 +- FOLLOW-UP | `CompletionHarnessId = HarnessDescriptor["id"]`への変更でU5内の現行5件unionは除去されたが、authoritativeな`component-methods.md`の`HarnessDescriptor.id`自体は7件の手書きunionであり、設計が主張する「registryから生成され、future harness追加時に手書きcontract union編集不要」は現状の上流契約からは導けない。algorithm forkは不要なのでREADYを単独では妨げないが、型生成元とregeneration手順を明記するか主張を「registry row・adapter・生成型更新」に修正するとよい。 + +## Review — Iteration 1 + +- **Verdict:** NOT-READY +- **Reviewer:** amadeus-architecture-reviewer-agent +- **Date:** 2026-08-03T15:01:06Z +- **Iteration:** 1 +- **Scope decision:** none + +completion evidenceのclosed wire、lock内再計算、registry由来ID型生成は解消済み。ただしterminal transaction自体の決定的identityとcommit後revision oracleが未定義で、同一plan replayとreceipt受理を一意に実装できない。 + +### Findings + +- BLOCKER | `TerminalCommitPlan`は`AuditTransaction.transactionId`を必須とし、同じplanのreplayで同じterminal receiptを返すと規定するが、transaction IDのcanonical domain・入力tuple・生成規則がない。上流`AuditTransaction`も単なる`{transactionId, expectedRevision}`でgeneratorを提供しない。また`acceptTerminalCommit`は`AuditCommitReceipt.stateProjectionRevision`をexact matchするとする一方、planにexpected post-commit revisionがなく、terminal transactionでrevisionが何段進むかのoracleも定義されていない。このため実装者ごとにrandom ID、event ID流用、revision増分解釈が分かれ、crash replayでsame receiptを保証できない。completion evidence ID/digest、ordered event identities、expected audit revisionを束縛したterminal transaction identityと、成功時のexpected state projection revisionを`TerminalCommitPlan`へ固定し、M07 lock・`acceptTerminalCommit`で再計算する必要がある。 + +## Review — Iteration 2 + +- **Verdict:** READY +- **Reviewer:** amadeus-architecture-reviewer-agent +- **Date:** 2026-08-03T15:04:12Z +- **Iteration:** 2 +- **Scope decision:** none + +最新修正は要求・設計・ルール・エンティティ間で整合している。terminal transaction IDはcompletion evidence、順序付きevent identities、audit/projection revisionへ決定的に束縛され、M07 lock内で再計算される。post-commit projection revisionはtransaction単位でsource+1に固定され、receipt受理とidempotent replayも閉じている。未解決BLOCKERはない。 + +### Findings + +- None diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/business-rules.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/business-rules.md new file mode 100644 index 000000000..650d8812a --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/business-rules.md @@ -0,0 +1,109 @@ +# Business Rules — five-harness-intent-completion + +## 上流入力と適用範囲 + +本規則は`units-generation/unit-of-work.md`、`units-generation/unit-of-work-story-map.md`、`requirements-analysis/requirements.md`、`application-design/components.md`、`application-design/component-methods.md`、`application-design/services.md`からU5 `five-harness-intent-completion`の不変条件を抽出する。対象はlive authorization、receipt validation、completion cohort、terminal transaction、persistence、package driftである。 + +## Cohort・registry規則 + +| ID | Rule | Violation | +|---|---|---| +| CMP-C01 | registryだけをcohort authoring sourceとし、Coreへharness名分岐を置かない | registry drift | +| CMP-C02 | GA cohortはclaude / codex / cursor / opencode / kimiのexactly five | contract failure | +| CMP-C03 | Kiro / Kiro IDEはregistryに保持するが今回のcohortへ含めない | cohort mismatch | +| CMP-C04 | future harnessはregistry capabilityとadapter追加だけで参加可能にする | architecture violation | +| CMP-C05 | empty / duplicate / unknown / capability不一致cohortを拒否する | malformed cohort | + +## Live authorization規則 + +| ID | Rule | Violation | +|---|---|---| +| CMP-A01 | credential-attested environmentだけがauthorization draftを作れる | unauthorized | +| CMP-A02 | protected authorization eventのcommit receipt確認前にlive runしない | provenance error | +| CMP-A03 | authorizationはIntent / harness / revision / package / registry / scenario / environment / trace / attestationへ束縛する | binding mismatch | +| CMP-A04 | credential / token / raw attestationをrecordへ保存しない | privacy violation | +| CMP-A05 | 同一authorization replayはsame receipt、binding変更は別identity | idempotency error | +| CMP-A06 | U5 authorization eventはregistry / scenarioを含む完全revisionをclosed payloadへ保存する | schema reject | + +## Scenario・receipt規則 + +- live scenarioは認可された一時workspaceでのみ実行し、不可逆な外部effectを持たない。 +- Judgeはpending requestと同じinvocation IDで`invokeOnce`し、request / result / trace一致を観測する。 +- election native path、またはrecommendationへのloud degradationをcanonical eventで観測する。 +- adapter自己申告booleanを成功根拠にせず、M08 validatorがcanonical auditを直接読む。 +- validation成功はprotected `LIVE_SMOKE_RECEIPT_VALIDATED` eventへcommitし、commit receipt確認済みのvalidationだけをcompletionへ使う。 +- `outcome=passed`、Judge観測、election / loud degradation観測の全条件を要求する。 +- `skipped / failed`、null observation、偽authorization、trace / environment / revision mismatchを拒否する。 + +## Completion規則 + +| ID | Rule | Violation | +|---|---|---| +| CMP-E01 | 全receiptは同じIntent / cohort / revision / package / registry / scenarioに一致する | evidence mismatch | +| CMP-E02 | cohort memberごとexactly oneのvalidated receiptを要求する | missing / duplicate | +| CMP-E03 | 入力順をidentityにせずcohort順へcanonicalizeする | nondeterminism | +| CMP-E04 | incompleteはmissing / rejected集合を返しsuccess eventを作らない | false completion | +| CMP-E05 | evidence identityは全receipt / authorization / source revisionを含む | identity error | +| CMP-E06 | evidenceはvalidation event / validation digest / Judge・election observation proof digestを全member分含む | evidence mismatch | +| CMP-E07 | completion evidence eventはexactly one `payload_v1`、closed field順、canonical payload / event digestを持つ | schema / identity error | + +## Terminal transaction規則 + +- complete evaluationだけがterminal planへ進める。 +- event順はcompletion evidence、optional grant completed、workflow null、`WORKFLOW_COMPLETED`で固定する。 +- fullのactive grantはcompletedへ遷移し、none / semiはgrant eventを作らない。 +- evaluatorがvalidation event snapshotから得たauthoritative audit revisionをM07 `AuditTransaction.expectedRevision`へ使い、caller入力を受けない。 +- evidence、mode / grant / workflow、revisionをappend lock内で再検証する。 +- completion evidence payload / digest / event identityをcanonical validation eventsからlock内再計算する。 +- terminal transaction IDはevidence ID / digest、cohort、順序付きevent IDs、expected audit revision、source projection revisionのcanonical tupleから生成・lock内再計算する。 +- state projection revisionは成功transactionごとexactly 1進み、planにsource+1のexpected post-commit revisionを固定する。 +- 全event commitを示すreceipt確認後だけcompleted resultを返す。 +- partial / conflict / unknown event / receipt mismatchではstateとresultを完了扱いにしない。 +- replayはsame event identities、transaction receipt、completion resultを返す。 +- 同一terminal transaction replayはprojection revisionを二重incrementしない。 + +## Persistence・privacy規則 + +| ID | Rule | +|---|---| +| CMP-P01 | session / process / compaction / clone後にcanonical auditからcompletion stateを復元する | +| CMP-P02 | runtime scratch / 一時workspace / timestampを正本やidentityにしない | +| CMP-P03 | raw credential / provider prompt / host payloadをaudit、status、OTelへ載せない | +| CMP-P04 | completed review extensionはcompletion evidence / seal / grant / workflowを変更しない | +| CMP-P05 | redaction failureはwithheldとして扱いraw fallbackしない | + +## Package・drift規則 + +- registryからpackage/setup/promote/self-install projectionとHarnessDescriptor ID unionを生成する。 +- current fiveのcontract adapterと`autonomyLive=true`をdrift guardで検証する。 +- Kiro系falseをpass代替にしない。 +- harness adapterへauthorization、completion evaluator、terminal state machineを複製しない。 +- contract testは全5件のexact receipt、negative oracle、canonical digestを比較する。 +- live credentialなしはskipとして可視化するが、completion evidenceへ寄与させない。 + +## Failure classification + +| Failure | Result | State | Recovery | +|---|---|---|---| +| registry / cohort malformed | error | unchanged | registry修正 | +| credential / authorizationなし | skipped / incomplete | runningまたはAWAITING_HUMAN | 認可済み環境で再実行 | +| live scenario failed | failed / incomplete | unchanged | evidence修正後に再実行 | +| receipt mismatch / forged | provenance error | unchanged | canonical receipt取得 | +| 1〜4harnessのみpass | incomplete | not completed | missing harness収集 | +| terminal CAS conflict | conflict | unchanged | snapshotから再評価 | +| commit receipt mismatch | error | completion未成立 | canonical transaction確認 | + +## 要件・AC追跡 + +| Rule group | Requirement / AC | +|---|---| +| cohort / registry / drift | FR-HAR-001、005〜007、2067-AC22、26 | +| authorization / scenario | FR-HAR-003、2067-AC23〜24 | +| receipt / completion | FR-HAR-002〜004、2067-AC22〜25 | +| terminal / persistence | FR-GRT-009、FR-STP-007、NFR-DET-002、NFR-REL-003 | +| privacy / review continuity | NFR-PRV-001〜002、FR-OBS-004 | + +## 非目標 + +- PR / merge、外部runner / supervisor、Kiro系live、credential保存。 +- production不可逆操作、harness別Core fork、新stage。 diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/domain-entities.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/domain-entities.md new file mode 100644 index 000000000..b7dccf05b --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/domain-entities.md @@ -0,0 +1,164 @@ +# Domain Entities — five-harness-intent-completion + +## 上流入力と境界 + +本モデルは`units-generation/unit-of-work.md`、`units-generation/unit-of-work-story-map.md`、`requirements-analysis/requirements.md`、`application-design/components.md`、`application-design/component-methods.md`、`application-design/services.md`を正本とする。M08がauthorization / registry / receipt / completion evidence、M09がraw live observation、M06がterminal orchestration、M04がgrant / workflow transition、M07がcanonical append / projectionを所有する。 + +```mermaid +flowchart LR + R["Validated Harness Registry"] --> C["Required Completion Cohort"] + C --> A["Committed Live Authorization"] + A --> L["Raw Intent Live Receipt"] + L --> V["Validated Intent Live Receipt"] + V --> E["Intent Completion Evidence"] + E --> T["Terminal Commit Plan"] + T --> X["Terminal Commit Receipt"] + X --> D["Completed Intent + Review Extension"] +``` + + + +## Entity catalog + +| Entity / Value Object | Kind | Identity | Owner | Persistence | +|---|---|---|---|---| +| `RequiredCompletionCohort` | Value Object | cohort digest | M08 | registry-derived | +| `LiveScenarioRevision` | Value Object | implementation + package + registry + scenario | M08 / M09 | receipt | +| `CommittedIntentLiveExecutionAuthorization` | Entity | authorization ID | M08 / M07 | full-revision protected event + commit receipt | +| `RawIntentLiveReceipt` | Value Object | receipt ID | M09 | validation input | +| `CanonicalLiveAuthorizationSnapshot` | Read Model | Intent + authorization + audit revision | M07 | canonical snapshot | +| `ValidatedIntentLiveReceipt` | Value Object | validation digest | M08 | completion evidence input | +| `CommittedValidatedIntentLiveReceipt` | Entity | validation event ID | M08 / M07 | protected validation event + commit receipt | +| `IntentCompletionEvidence` | Entity | evidence ID | M08 | canonical audit | +| `CompletionEvidenceValidatedEventPlan` | Value Object | evidence event ID | M08 / M07 | terminal transaction first event | +| `IntentCompletionCheck` | Value Object | Intent + cohort + revision | M08 | transient / event plan | +| `TerminalCommitPlan` | Value Object | transaction ID | M06 | append command | +| `TerminalCommitReceipt` | Entity | evidence + transaction | M06 / M07 | canonical completion projection | + +## RequiredCompletionCohort + +| Attribute | Invariant | +|---|---| +| `schemaVersion / cohortId` | closed v1 / `intent-autonomy-live` | +| `harnessIds` | registry-derived、sorted、unique、non-empty | +| `registryDigest` | validated 7-row registryと一致 | +| `cohortDigest` | schema / ID / registry / membersへ束縛 | + +GA instanceのmemberはClaude Code、Codex、Cursor、OpenCode、Kimi Codeの5件である。harness ID型はregistry生成`HarnessDescriptor["id"]`を参照し、型とalgorithmはregistry member配列を処理するため、future harness名をCore分岐や手書きunionへ追加しない。Kiro / Kiro IDEはregistry rowを持つがcapability falseのため非memberである。 + +## LiveScenarioRevision + +`implementationRevision`、`packageDigest`、`registryDigest`、`scenarioDigest`をすべて持つ。いずれか一つでも異なるreceiptを同じcompletionへ混在させない。Git branch名、worktree path、timestampはrevision identityに含めない。 + +## CommittedIntentLiveExecutionAuthorization + +authorizationはIntent、harness、cohort、implementation / package / registry / scenario revision、environment、issuer principal、trace / span、attestation digest、authorization payload digest、event identity、commit receiptを持つ。U1 portのbase revisionをU5 serviceがvalidated registry / cohort / scenarioへ拡張し、closed `LIVE_SMOKE_AUTHORIZED.payload_v1`へ保存する。commit receiptがevent identityを含まなければuncommittedであり、M09 inputへ昇格しない。 + +credential / token / raw environment valueはentity attributeに存在しない。`environmentId`と`attestationDigest`はsafe stable referenceである。 + +## RawIntentLiveReceipt + +| Attribute | Invariant | +|---|---| +| `receiptId` | authorization / revision / observation digestから決定 | +| `harnessId` | cohort member | +| `authorization*` | committed authorizationとexact match | +| `revision` | requested live scenarioとexact match | +| `environment / trace / span / attestation` | authorizationとexact match | +| `outcome` | passed / skipped / failed | +| `observation` | passed時だけnon-null | + +`LiveObservation`はJudge invocation IDとelection decision IDを持つ。loud degradation時はsafe reasonを必須、elected時はreason=nullとする。raw receiptはM09の観測結果であり、それだけではcompletion evidenceにならない。 + +## CanonicalLiveAuthorizationSnapshot + +M07 adapterが同一snapshotでcanonical audit、authoritative audit revision、state projection revision、committed authorizationを返す。caller提供audit、event count、最大shard sequenceからrevisionを作らない。M08 validatorはこのsnapshotだけをcanonical sourceとして使用する。 + +## ValidatedIntentLiveReceipt + +raw receiptの全fieldに加え`outcome=passed`、non-null observation、observation proof digest、validation digestを持つ。validation digestはfull-revision authorization event、commit transaction、canonical Judge / decision observations、source audit revisionへ束縛する。 + +同じraw receiptとcanonical snapshotは同じvalidation event planを返す。authorizationやauditが変化した場合は再検証し、古いvalidation digestをbearer tokenとして信用しない。 + +## CommittedValidatedIntentLiveReceipt + +M08 validatorはclosed `LIVE_SMOKE_RECEIPT_VALIDATED.payload_v1` eventを計画し、M07 commit receiptがそのevent identityを含む場合だけcommitted validationへ昇格する。payloadはharness / receipt / authorization、完全revision、validation / observation proof digest、Judge invocation、election decision / outcome、authorization source revisionを持つ。 + +completion evaluatorはvalidation object配列を信用せず、validation event IDsからM07 adapterの同一snapshot readでcommitted validation集合を再構築する。 + +## IntentCompletionEvidence + +| Attribute | Invariant | +|---|---| +| `intentUuid` | 全receiptと同じtarget | +| `cohort / revision` | 全receiptとexact match | +| `receiptIds / authorizationIds` | cohort順、件数=member数、unique | +| `validationEventIdentities / validationDigests` | committed validationとexact match | +| `observationProofDigests` | Judge / election proofを全member分束縛 | +| `sourceAuditRevisions` | 各validation snapshotのauthoritative revision | +| `evidenceId / evidenceDigest` | 全closed fieldへcanonical binding | + +evidenceは全memberが揃った場合だけ存在する。1〜4件、skip、duplicate、別revisionを補完・推測しない。 + +## CompletionEvidenceValidatedEventPlan + +event typeはexactly `LIVE_COMPLETION_EVIDENCE_VALIDATED`、fieldsはexactly one `payload_v1`である。payloadはevidence / Intent / cohort、完全revision、cohort順のharness / receipt / authorization / validation event / validation digest / observation proof / source revision、evidence digest、evaluator snapshot revisionをclosed field順で持つ。 + +payload digestはschema-separated canonical value digest、event identityはIntent / evidence / cohort / source revision / payload digestのcanonical tupleである。M07はterminal lock内でvalidation eventsからpayload、evidence digest、event identityを再計算し、unknown / missing field、配列長・順序不一致、stale revisionを拒否する。 + +## TerminalCommitPlan + +planはM07 `AuditTransaction`、順序付きevent、completion evidence、expected event identitiesを持つ。full modeだけgrant-completed eventを含む。workflow-null eventと`WORKFLOW_COMPLETED`は全modeで必須である。 + +expected audit / state projection revisionはcompletion evaluatorがcommitted validation eventsを読んだauthoritative snapshotから得て、caller入力を受けない。transaction IDはIntent、completion evidence ID / digest、cohort digest、順序付きevent identities、expected audit revision、source state projection revisionの`amadeus.intent-terminal-transaction.v1` tupleから決定する。expected post-commit state projection revisionはsource + 1であり、event件数へ依存しない。M07 lock内でtransaction IDとCASを再検証し、失敗すればplan全体をcommitしない。 + +## TerminalCommitReceipt + +commit receiptが再計算したtransaction IDと全expected event identitiesを含み、projection revisionがplanのexpected post-commit revisionに一致した場合だけ作る。resultは`outcome=completed`に限定する。同一transaction replayは同じreceiptを返しrevisionを再度進めない。 + +terminal receiptから次を再構築できる。 + +- completion evidence / cohort / revision +- grant completedまたはgrant null +- workflow execution state=null +- completion seal / result identity +- committed event集合 + +completed後のU4 review extensionは別hash chainであり、このreceiptを変更しない。 + +## Lifecycle + +```text +registry-validated + -> authorization-planned + -> authorization-committed + -> live-observed + -> receipt-validated + -> cohort-incomplete | cohort-complete + -> terminal-planned + -> terminal-committed + -> completed +``` + +各矢印はtyped resultまたはcanonical audit eventである。skip / failure / mismatchは前のvalid stateを変更しない。runtime scratch消失後もaudit replayで同じstateへ戻る。 + +## Harness・persistence boundary + +5harness adapterはauthorization port、native invocation、observation extractionだけを実装する。cohort resolver、validator、completion evidence、terminal state machineは共有M08 / M06実装である。Harness ID unionはregistry authoring sourceからpackage生成時に再生成し、手書きCore unionを増やさない。 + +session / process / compaction / clone reload fixtureは、authorization、validated receipt、completion evidence、terminal receipt、U4 review extension headのcanonical-value digestを比較する。live一時workspaceは再生正本ではない。 + +## 要件追跡 + +| Entity group | Requirement / AC | +|---|---| +| cohort / revision | FR-HAR-001、005〜007、2067-AC22、26 | +| authorization / raw receipt | FR-HAR-003、2067-AC23〜24 | +| validated receipt / evidence | FR-HAR-002〜004、2067-AC22〜25 | +| terminal plan / receipt | FR-GRT-009、FR-STP-007、NFR-DET-002、NFR-REL-003 | +| privacy / review extension | NFR-PRV-001〜002、FR-OBS-004 | + +## 非目標 + +- PR / merge entity、runner / supervisor lifecycle、credential entity。 +- Kiro / Kiro IDE live receipt、harness固有completion state machine。 diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/functional-design-questions.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/functional-design-questions.md new file mode 100644 index 000000000..5de744fbf --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/functional-design/functional-design-questions.md @@ -0,0 +1,24 @@ +# Functional Design Questions — five-harness-intent-completion + +## 上流入力 + +`units-generation/unit-of-work.md`、`units-generation/unit-of-work-story-map.md`、`requirements-analysis/requirements.md`、`application-design/components.md`、`application-design/component-methods.md`、`application-design/services.md`を照合した。 + +## 確認結果 + +要求レベルの未決事項はない。Issueどおり、次を確定事項として設計する。 + +- 現行対象はClaude Code、Codex、Cursor、OpenCode、Kimi Codeの5harnessである。Kiro / Kiro IDEはregistryに残すが、今回のcompletion cohortへ含めない。 +- live smokeはcredential-attestedな明示認可を必須とし、secret / tokenそのものをrecordへ保存しない。 +- `skipped / failed`、Judge未観測、electionまたはloud degradation未観測、revision / package / registry / environment / trace / attestation不一致はpassにしない。 +- 5harnessすべての検証済みreceiptが同じIntent・実装revision・package digest・registry digest・scenario digestへ一致した場合だけIntent terminal transitionを計画する。 +- completion transactionはgrant completion、workflow null、`WORKFLOW_COMPLETED`を原子的に確定し、commit receipt確認後だけcompleted resultを返す。 +- session / process / compaction / clone後もreceipt集合とterminal stateをcanonical auditから復元する。 +- completed Intentのdecision reviewはU4のpost-seal review extensionで継続できる。 +- PR、merge、runner / supervisor、Kiro系live対応は完了条件へ含めない。 + +Issue外の矛盾・抜け漏れは検出していないため、追加の人間判断は不要である。 + +## Optional artifact判断 + +`frontend-components.md`は生成しない。U5は既存CLI / Bun live runner / audit transaction / harness registryのbehaviorであり、新規frontend componentを含まない。 diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/logical-components.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/logical-components.md new file mode 100644 index 000000000..fccb59ec0 --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/logical-components.md @@ -0,0 +1,43 @@ +# Logical Components — five-harness-intent-completion + +## 入力と境界 + +本設計は`functional-design/business-logic-model.md`を正本とする。`performance-requirements.md`、`security-requirements.md`、`scalability-requirements.md`、`reliability-requirements.md`、`tech-stack-decisions.md`はexpected absenceである。 + +## Component inventory + +| Component | Owns | Isolation | +| --- | --- | --- | +| `CompletionCohortResolver` (M08) | registry capability、sorted cohort、cohort digest | descriptor順や手書きharness unionへ依存しない | +| `IntentLiveAuthorizationService` (U5/M06) | full revision authorization、protected event plan、commit binding | credential値をCoreへ持ち込まない | +| `IntentLiveRunCoordinator` (U5/M06) | persistent reservation、commit binding、canonical reconcile、dispatch claim CAS、attempt budget | uncommitted / stale / already-claimed stateからdispatchしない | +| `CanonicalLiveRunStateReader` / `NoEffectProofVerifier` (M07 adapter) | current run head、commit receipt、operation proofの再検証 | caller proof digestをauthorityにしない | +| `HarnessRuntimeAdapter` (M09) | operation + attempt idempotency、native install / invocation、Judge / decision observation抽出 | 同一dispatch keyをexactly one operationへ線形化 | +| `IntentLiveReceiptValidator` (M08) | canonical authorization / observation検証、validation event plan | caller自己申告をauthorityにしない | +| `CanonicalLiveEvidenceReader` (M07 adapter) | authorization / audit / receiptの同一snapshot read | explicit Intent partitionだけを読む | +| `IntentCompletionEvaluator` (M08) | cohort全件評価、completion evidence plan | validation event identities以外を入力にしない | +| `TerminalCommitPlanner` (M06/M04) | ordered terminal events、deterministic transaction identity | incomplete evidenceを受理しない | +| `ProtectedTerminalAppender` (M07) | lock内再検証、CAS、atomic append、idempotency | 部分commitを公開しない | +| `HarnessPackagingProjection` | registryから生成型 / distribution / adapter unionを再生成 | Core algorithmをharness別forkしない | + +## Dependency direction + +`Harness Registry → Cohort Resolver → Authorization Service → Live Run Coordinator → Native Adapter → Receipt Validator → Completion Evaluator → Terminal Planner → Protected Appender`の方向に限定する。M08 / M06はM07を直接importせずcanonical reader / appender portを使い、M09 adapterはevaluatorやterminal eventを生成しない。 + +## Failure domains + +- credential / environment不備: 対象harness authorizationまたはrunだけを失敗させる。 +- native adapter不備: safe failed receiptまでに閉じ、他harnessのvalidation eventを変更しない。 +- native effect uncertainty: reconcile proofがattested no-effectでない限りrunをincompleteへterminal化し、Judgeを再invokeしない。 +- receipt validation不備: validation eventを作らずIntentをincompleteに保つ。 +- cohort / revision drift: evidenceを作らず新snapshotから再評価する。 +- terminal CAS / parser不備: terminal transaction全件を拒否し、grant / workflow / sealを変更しない。 +- packaging drift: generated distribution checkを失敗させ、Core runtime contractを変更しない。 + +## Observability + +Intent、harness、authorization、receipt、validation、evidence、transaction、trace / spanのsafe ID / digestを既存Event Registry / OTelへ載せる。credential、raw prompt、raw command、attestation materialを載せない。statusはmissing / rejected harness IDをsafe enumとして示すが、secretやenvironment detailを公開しない。 + +## Verification + +component contract fixtureは現行5 harnessで同じcohort / canonical vector / terminal resultを検証する。session / process / compaction / clone reload、future harness追加、partial failure、same-plan replayでdependency bypassや別adapter内Core実装がないことを確認する。 diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/nfr-design-questions.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/nfr-design-questions.md new file mode 100644 index 000000000..67f4f1fea --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/nfr-design-questions.md @@ -0,0 +1,19 @@ +# NFR Design 質問 — five-harness-intent-completion + +## 裁定結果 + +追加のユーザー裁定は不要である。`performance-requirements.md`、`security-requirements.md`、`scalability-requirements.md`、`reliability-requirements.md`、`tech-stack-decisions.md`はexpected absenceであり、`functional-design/business-logic-model.md`のFR-HAR-001〜007 / 2067-AC22〜26契約をNFRオラクルとする。 + +## 確認 + +Q1. live実行をIntent完了の唯一条件にするか。 + +[Answer]: しない。必須cohort全件のcredential-attested live receiptがcanonical auditで検証済みの場合だけterminal transitionを許可し、credential不足のskipは完了証拠へ昇格させない。 + +Q2. 将来のharness追加でCore algorithmを変更するか。 + +[Answer]: しない。registry row、native adapter、生成型 / distributionの更新に限定し、cohort evaluatorとterminal transactionはharness名や件数を分岐へ埋め込まない。 + +## Reviewer上限到達後の裁定 + +[Answer]: commit receiptでbindしたcanonical run stateとbranded dispatch permitを公開契約へ追加し、新しいreview cycleで確認する。 diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/performance-design.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/performance-design.md new file mode 100644 index 000000000..04070d6ea --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/performance-design.md @@ -0,0 +1,55 @@ +# Performance Design — five-harness-intent-completion + +## 入力と性能オラクル + +本設計は`functional-design/business-logic-model.md`を正本とする。`performance-requirements.md`、`security-requirements.md`、`scalability-requirements.md`、`reliability-requirements.md`、`tech-stack-decisions.md`はexpected absenceであり、根拠のない数値SLOや常駐runnerを追加しない。 + +性能オラクルはIntent partition内のbounded cohort評価、harness単位のlive実行分離、canonical auditのincremental receipt検証である。全Intent、全artifact、全harness配布物の横断scanをterminal checkごとに行わない。 + +## Live authorizationと実行 + +M06はregistryから解決したcohortをharness ID順へ正規化し、各memberのauthorizationを1回ずつprotected appendする。commit済みauthorization同士に依存はないためnative scenarioはharness単位で並行実行できるが、receipt validation eventは各Intent partitionへidempotentにappendする。 + +live adapterはpackage install、Judge invoke-once、decision / degradation observationだけを所有する。Core algorithmや期待値をadapterへ複製せず、credential値やraw provider payloadを転送・保持しない。 + +## Evaluation complexity + +M08 evaluatorは指定されたvalidation event identityだけをcanonical readerへ渡し、cohort memberごとexactly oneへ正規化する。cohort解決は`O(H log H)`、receipt検証とevidence組立は`O(H)`、追加memoryは`O(H)`とする。`H`はregistry由来であり、現行5件を定数分岐へ埋め込まない。 + +duplicate、missing、別revision、cohort外receiptは全量fallbackせずincompleteまたはclosed `ContractError`へ閉じる。skip / failed receiptをpassとして数えない。 + +## Terminal transaction + +terminal planはevaluation snapshotのaudit / state projection revisionを再利用し、lock内で全validation eventを再読する。transaction identityとordered event identitiesを事前計算し、成功時はaudit transaction単位でprojection revisionをexactly 1進める。CAS競合時は再scanで推測せず、新snapshotからevaluationをやり直す。 + +## Verification + +現行5 harness exact tuple、receipt入力順変更、duplicate / missing / skipped / forged authorization、parallel completion、CAS drift、same-plan replayをfixture化する。成功時のreceipt集合・evidence digest・transaction ID・result digestが全harnessで一致し、失敗時にterminal eventが0件であることを要求する。 + +## Review — Iteration 1 + +- **Verdict:** NOT-READY +- **Reviewer:** amadeus-architecture-reviewer-agent +- **Date:** 2026-08-03T16:01:48Z +- **Iteration:** 1 +- **Scope decision:** none + +公開call shapeはcommit済みrun state、canonical reader、proof verifier、branded permitへ改善された。しかしpermit消費の原子claimがなく、並行process/cloneによる同一attemptの重複dispatchを排除できない。 + +### Findings + +- BLOCKER | RunDispatchPermitのsingle-use性がcanonicalに実装できない。bindTransitionCommitは同じcommit receiptとcurrent authorized stateから複数回同一permitを再構築でき、dispatchの直前再読もrun headを変更しないため、2 process/cloneが同時に同じstartedまたはredispatch-authorized headを読み、双方がrevalidationを通過してnative portを呼べる。設計はreused permit拒否とJudge invocation重複0を要求するが、dispatch-consumed/claimed event、atomic CAS、またはoperation+attemptをキーにしたnative dispatchの厳密なidempotency contractがない。permit IDの消費をeffect前にcanonical CASで一度だけclaimするか、native portが同一operation・attemptをexactly one operationへ畳み込むauthoritative idempotency receiptを定義し、競合callerをreconcileへ送る必要がある。 + +## Review — Iteration 2 + +- **Verdict:** READY +- **Reviewer:** amadeus-architecture-reviewer-agent +- **Date:** 2026-08-03T16:03:29Z +- **Iteration:** 2 +- **Scope decision:** none + +dispatch permitはcurrent-head CASによるLIVE_SMOKE_RUN_DISPATCH_CLAIMEDへ一度だけ束縛され、競合process/cloneはreconcileへ戻る。さらにoperation+attemptのauthoritative idempotency keyがnative側の並行・再送を同一operationへ線形化するため、重複Judge実行と予算再消費を防止できる。全7成果物は整合し、未解決BLOCKERや具体的な循環依存はない。 + +### Findings + +- None diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/reliability-design.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/reliability-design.md new file mode 100644 index 000000000..486cdd48a --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/reliability-design.md @@ -0,0 +1,35 @@ +# Reliability Design — five-harness-intent-completion + +## 入力とfailure semantics + +本設計は`functional-design/business-logic-model.md`を正本とする。`performance-requirements.md`、`security-requirements.md`、`scalability-requirements.md`、`reliability-requirements.md`、`tech-stack-decisions.md`はexpected absenceである。 + +live authorization、native observation、receipt validation、cohort evaluation、terminal commitを別checkpointにし、各境界をcanonical event / commit receiptで再開可能にする。runtime scratchや一時workspaceを正本にしない。 + +## Crash and replay + +authorization append前crashは未認可、append後crashは同じauthorization ID / commit receiptから再開する。native effect前にharness単位の`LIVE_SMOKE_RUN_RESERVED`をcommitし、stable run ID / operation reference / Judge invocation ID、`maxDispatches=2`を保存する。resumeは常に同じoperationをreconcileし、completedならdispatchせずreceiptを回収する。 + +coordinatorはcaller提供run stateやproofを受けずcanonical current runを再読する。reservation / started / redispatch transitionごとにM07 commit receiptを検証して`CommittedIntentLiveRunState`へbindし、startedまたはredispatch-authorized eventがcanonical headである場合だけbranded dispatch permitを発行する。native portはこのpermit以外のdispatch overloadを持たない。 + +dispatch permitはeffectを直接許可しない。coordinatorはpermit ID / operation / attempt / authorized headを含む`LIVE_SMOKE_RUN_DISPATCH_CLAIMED`をM07のcurrent-head CASでappendし、commit receipt検証後の`ClaimedRunDispatch`だけをnative portへ渡す。並行claimのloserは`CONFLICT(dispatchClaim)`から同じoperationのreconcileへ戻る。 + +native portはoperation reference + attemptのdomain-separated digestをauthoritative idempotency keyにし、並行・再送をexactly one native operationへ線形化する。重複callerは同じnative operation IDへattachし、別Judgeを起動しない。dispatch receiptのkey / operation / proofはcoordinatorがcanonical claimと照合する。 + +初回started後にnative側がattested no-effectを返し、専用proof verifierがcanonical operation logへexact matchした場合だけ、永続`LIVE_SMOKE_RUN_REDISPATCH_AUTHORIZED`でattempt 2を消費して最大1回再dispatchする。dispatch直前にもpermitとcanonical head / budgetを再検証する。effect-possible / unknown、またはattempt 2後の未回収は`LIVE_SMOKE_RUN_INCOMPLETE`へterminal化し、追加dispatchせずcohortをincompleteに保つ。validation append後はcanonical validation eventを再利用する。同一event identity / payloadはidempotent、同一identityの異payloadは`CONFLICT`とする。 + +evaluatorはcanonical validation set snapshotだけからcomplete / incompleteを返す。missing、skipped、failed、別revision receiptを補完・推測せず、incomplete時はcompletion evidence eventを作らない。 + +## Atomic terminal transition + +terminal event順は`LIVE_COMPLETION_EVIDENCE_VALIDATED`、nullable grant-completed、workflow-state-null、`WORKFLOW_COMPLETED`に固定する。transaction IDはevidence、cohort、ordered event identities、audit / projection revisionへ束縛し、M07 lock内で再計算する。 + +全event appendまたは全件拒否とし、grantだけcompleted、workflow stateだけnull、completion eventだけ存在する部分状態を公開しない。same plan replayはidempotency indexから同じcommit / terminal receiptを返し、revisionを再度進めない。 + +## Persistence + +session / process / compaction / clone後にcohort、revision、authorization、live run reservation / attempt budget / terminal run state、validation集合、observation proof、evidence digest、grant、workflow result、completion sealを再構築する。completed後のU4 review extensionはterminal identityを変更しない。 + +## Failure injection + +reservation / started / redispatch authorization / dispatch claim / completed / incompleteの各append直前・直後、並行clone claim、native idempotency key競合、Judge invoke直後、native process終了、clone merge、CAS drift、registry / scenario drift、partial receipt、duplicate ID、projection revision overflowへfailureを注入する。native operation / Judge invocation重複0、attempt 3発行0、unknown後dispatch 0、partial terminal 0、duplicate completion 0、same-plan receipt差分0、skipからcompletionへの昇格0を要求する。 diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/scalability-design.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/scalability-design.md new file mode 100644 index 000000000..0aee422c2 --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/scalability-design.md @@ -0,0 +1,25 @@ +# Scalability Design — five-harness-intent-completion + +## 入力とscaling axis + +本設計は`functional-design/business-logic-model.md`を正本とする。`performance-requirements.md`、`security-requirements.md`、`scalability-requirements.md`、`reliability-requirements.md`、`tech-stack-decisions.md`はexpected absenceである。 + +scaling axisはIntent partition数とregistry由来cohort member数である。常駐service、global coordinator、別databaseを追加せず、短命CLIとcanonical auditを維持する。 + +## Intent partitioning + +authorization、receipt validation、completion evidence、terminal transactionはexplicit Intent UUIDでpartitionする。別Intentのreceipt IDからtargetを逆引きせず、cross-Intent混入を拒否する。Intent間のlive scenarioは独立に進められるが、同一Intentのterminal appendだけを1 lock / CASへ直列化する。 + +## Cohort growth + +cohort membershipは`packages/framework/harness/registry.ts`のcapabilityから解決し、harness ID順へ正規化する。現行GA fixtureはClaude Code、Codex、Cursor、OpenCode、Kimi Codeをexact oracleとし、Kiro / Kiro IDEはcapability falseのため除外する。 + +将来harnessはregistry row、credential-attested native adapter、生成型 / distributionを追加して参加する。Core evaluator、authorization schema、terminal transactionへharness名や件数分岐を追加しない。invalid / duplicate / empty cohortはfail-closedする。 + +## Backpressureとisolation + +native scenarioの並行度は実行環境の既存bounded schedulerへ委ね、無制限fan-outを作らない。1 harnessのtimeout / skip / failedはそのreceiptだけを失敗させ、他harnessのvalidated receiptを破棄しない一方、全memberが揃うまでIntent completionを許可しない。 + +## Verification + +registry順序変更、future harness fixture、cohort member追加 / 削除、複数Intent並行、1 harness timeout、duplicate validation、clone merge後の再評価を検査する。adapter追加が既存5 harnessのCore fixtureとterminal digestを分岐させないことをdrift guardで確認する。 diff --git a/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/security-design.md b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/security-design.md new file mode 100644 index 000000000..8cbc9b082 --- /dev/null +++ b/amadeus/spaces/default/intents/260803-intent-autonomy/construction/five-harness-intent-completion/nfr-design/security-design.md @@ -0,0 +1,27 @@ +# Security Design — five-harness-intent-completion + +## 入力とtrust boundary + +本設計は`functional-design/business-logic-model.md`を正本とする。`performance-requirements.md`、`security-requirements.md`、`scalability-requirements.md`、`reliability-requirements.md`、`tech-stack-decisions.md`はexpected absenceである。 + +trust boundaryはcredential-attested environment、protected authorization / validation append、canonical evidence reader、terminal append lockである。caller提供audit、receipt配列、registry membership、revision、observation booleanをauthorityとして信用しない。 + +## Credentialとauthorization + +`LiveAuthorizationPort`はcredential値を返さず、safe environment ID、issuer principal、trace / span、attestation digestだけを返す。U5 serviceはIntent、harness、cohort、implementation / package / registry / scenario revisionをclosed `LIVE_SMOKE_AUTHORIZED.payload_v1`へ束縛する。commit receiptを検証する前にnative scenarioを開始しない。 + +credential、token、raw command、raw provider prompt、未redact tool payloadをaudit、receipt、status、telemetry、artifactへ保存しない。redactionまたはattestation生成に失敗した場合はauthorizationを発行せず、raw fallbackを禁止する。 + +## Receipt proof + +M08 validatorはM07 adapterからauthorization event、commit receipt、Judge request / result、election decisionまたはloud degradationを同一canonical snapshotで読む。Intent / harness / authorization / environment / trace / revisionをexact matchし、adapter自己申告の`judgeObserved`を単独証拠にしない。 + +unknown / missing field、duplicate event、forged authorization、trace mismatch、null observation、skipped / failed outcomeはvalidation eventを作らない。validation digestとobservation proof digestをprotected `LIVE_SMOKE_RECEIPT_VALIDATED`へ保存し、terminal lockで再計算する。 + +## Least privilegeとprivacy + +live workspaceは一時的かつ不可逆な外部effectを持たない。adapter権限は対象harnessのnative invocation、same-operation reconciliation、safe observation抽出へ限定し、grant変更、PR / merge、外部deploymentを許可しない。operation reference / Judge invocation IDはeffect前にprotected run reservationへ保存する。dispatch permitはcanonical CAS claim後の`ClaimedRunDispatch`へbindし、native portはoperation + attempt idempotency keyでexactly one operationへ線形化する。attested no-effectは専用proof verifierがcanonical operation logへ一致させる。Event Registry / OTelはIntent、harness、authorization、run / operation、receipt、evidence、transaction、traceのsafe ID / digestだけを相関可能にする。 + +## Verification + +credential不足、issuer mismatch、revision drift、replayed attestation、cross-Intent receipt、forged Judge observation、raw secret混入、validation event改変をnegative fixtureにする。どの場合もcompletion evidence、grant completion、`WORKFLOW_COMPLETED`を0件とする。 diff --git a/docs/reference/12-state-machine.md b/docs/reference/12-state-machine.md index 9da29469f..f3e85253a 100644 --- a/docs/reference/12-state-machine.md +++ b/docs/reference/12-state-machine.md @@ -396,6 +396,7 @@ The Loop Monitor commits delivery observations, cycle triggers, Judge reservatio | `QUALITY_REPAIR_TRANSACTION_COMMITTED` | `tools/amadeus-quality-repair-replay.ts` | One quality snapshot, progress, replan, stall, or resume transaction and its generic Monitor effects committed atomically | | `INTENT_AUTONOMY_TRANSACTION_COMMITTED` | `tools/amadeus-intent-autonomy-replay.ts` | One Intent-scoped mode, grant, decision, workflow-effect, park, resume, or invocation-failure transaction committed atomically | | `AUTO_DECISION_REVIEWED` | `tools/amadeus-autonomy-review-production.ts` | A human accepted or flagged one immutable auto decision on the review surface (append-only; never replays the decided effect) | +| `INTENT_COMPLETION_TRANSACTION_COMMITTED` | `tools/amadeus-intent-completion.ts` | The Core Intent completion transaction committed atomically, sealing the Intent record with its evidence digest | ### Swarm diff --git a/packages/framework/core/knowledge/amadeus-shared/audit-format.md b/packages/framework/core/knowledge/amadeus-shared/audit-format.md index d0d78f06e..2fd599fa9 100644 --- a/packages/framework/core/knowledge/amadeus-shared/audit-format.md +++ b/packages/framework/core/knowledge/amadeus-shared/audit-format.md @@ -29,7 +29,7 @@ tools read them from. Neither lists the record envelope — every record carries (the park pair, the practices events) show it in the table as the attribute it is. -## Event Registry (89 events, 21 categories) +## Event Registry (90 events, 21 categories) ### Workflow Lifecycle (7 events) @@ -257,6 +257,7 @@ The event set is the atomic canonical stream for delivery observation, cycle tri | `QUALITY_REPAIR_TRANSACTION_COMMITTED` | One Quality snapshot/progress/replan/stall/resume transaction and its generic Monitor effects commit atomically | Quality Scope Id, Transaction Id, Transaction | — | `tools/amadeus-quality-repair-replay.ts` | | `INTENT_AUTONOMY_TRANSACTION_COMMITTED` | One Intent-scoped mode/grant/decision/effect/park transaction commits atomically | Intent Uuid, Transaction Id, Transaction Digest, Transaction | Principal, Decider, Actor, Basis | `tools/amadeus-intent-autonomy-replay.ts` | | `AUTO_DECISION_REVIEWED` | A real human accepts or flags one immutable automatic decision; completed Intent reviews extend the review chain without changing the completion seal | Intent Uuid, Decision Id, Review Id, Choice, Lifecycle, Review Principal, Review Actor, Source Human Turn, Audit Transaction Id, Payload Digest, Payload V1 | Decision Principal, Decision Actor, Decision Source, Basis Digest, Grant Id, Remediation, Note Digest, Redaction Status, Event Identity, Projection Revision, Trace Id, Span Id | `tools/amadeus-autonomy-review-production.ts` | +| `INTENT_COMPLETION_TRANSACTION_COMMITTED` | The Core Intent completion transaction commits and seals the Intent record with its evidence digest | Intent Uuid, Transaction Id, Evidence Id, Evidence Digest, Completion Seal Digest, Transaction | — | `tools/amadeus-intent-completion.ts` | ### Swarm (7 events) diff --git a/packages/framework/core/otel/event-registry.ts b/packages/framework/core/otel/event-registry.ts index 349dcf584..2916aba59 100644 --- a/packages/framework/core/otel/event-registry.ts +++ b/packages/framework/core/otel/event-registry.ts @@ -1,6 +1,6 @@ // event-registry.ts — the typed Event Registry (FR-EVT-1). // -// The canonical half of the registry covers the full 89-event audit +// The canonical half of the registry covers the full 90-event audit // vocabulary (#1672; VALID_EVENT_TYPES in tools/amadeus-audit.ts) — every // canonical name maps 1:1 onto the EXISTING v1 audit event vocabulary so the // current readers understand the records unchanged. The telemetry half @@ -76,7 +76,7 @@ export type EventDef = { // The canonical cardinality (#1672). The drift guard pins this so an emptied // or truncated registry fails instead of passing vacuously. -export const EXPECTED_CANONICAL_COUNT = 89; +export const EXPECTED_CANONICAL_COUNT = 90; // The OTel semantic-convention span event name produced by recordException(). // Registered as telemetry (FR-EVT-7): it rides the span record, never the @@ -224,6 +224,22 @@ export const REGISTERED_EVENTS = [ optionalAttributes: ["Principal", "Decider", "Actor", "Basis"], schemaVersion: 1, }, + { + name: "amadeus.intent_completion.transaction.committed", + auditEvent: "INTENT_COMPLETION_TRANSACTION_COMMITTED", + durability: "canonical", + category: "grant", + requiredAttributes: [ + "Intent Uuid", + "Transaction Id", + "Evidence Id", + "Evidence Digest", + "Completion Seal Digest", + "Transaction", + ], + optionalAttributes: [], + schemaVersion: 1, + }, { name: "amadeus.auto_decision.reviewed", auditEvent: "AUTO_DECISION_REVIEWED", diff --git a/packages/framework/core/tools/amadeus-audit.ts b/packages/framework/core/tools/amadeus-audit.ts index 539055c5f..5984df91d 100644 --- a/packages/framework/core/tools/amadeus-audit.ts +++ b/packages/framework/core/tools/amadeus-audit.ts @@ -94,6 +94,7 @@ const VALID_EVENT_TYPES = new Set([ "QUALITY_REPAIR_TRANSACTION_COMMITTED", "INTENT_AUTONOMY_TRANSACTION_COMMITTED", "AUTO_DECISION_REVIEWED", + "INTENT_COMPLETION_TRANSACTION_COMMITTED", // Session events (hook-owned) "SESSION_STARTED", "SESSION_RESUMED", @@ -239,6 +240,7 @@ export const EVENT_HEADINGS: Record = { QUALITY_REPAIR_TRANSACTION_COMMITTED: "Quality Repair Transaction Committed", INTENT_AUTONOMY_TRANSACTION_COMMITTED: "Intent Autonomy Transaction Committed", AUTO_DECISION_REVIEWED: "Auto Decision Reviewed", + INTENT_COMPLETION_TRANSACTION_COMMITTED: "Intent Completion Transaction Committed", UNIT_POOL_EVENT_SET_COMMITTED: "Unit Pool Event Set Committed", SESSION_STARTED: "Session Start", SESSION_RESUMED: "Session Resume", diff --git a/packages/framework/core/tools/amadeus-harness-registry.ts b/packages/framework/core/tools/amadeus-harness-registry.ts index 43eafc1b2..3d6e8a190 100644 --- a/packages/framework/core/tools/amadeus-harness-registry.ts +++ b/packages/framework/core/tools/amadeus-harness-registry.ts @@ -63,3 +63,12 @@ export const PACKAGE_HARNESS_IDS: readonly PackageHarnessId[] = HARNESS_REGISTRY export const SELF_INSTALL_HARNESS_IDS: readonly SelfInstallHarnessId[] = HARNESS_REGISTRY .filter((descriptor): descriptor is RegistryRow & { readonly selfInstallFace: true } => descriptor.selfInstallFace) .map((descriptor) => descriptor.id as SelfInstallHarnessId); + +// The registry paired with its content digest, as intent completion consumes +// it (amadeus-intent-completion.ts): revision and cohort bindings pin the +// registryDigest so a cohort minted against one registry cannot be replayed +// against another. +export interface ValidatedHarnessRegistry { + readonly descriptors: readonly HarnessDescriptor[]; + readonly registryDigest: string; +} diff --git a/packages/framework/core/tools/amadeus-intent-autonomy-runtime.ts b/packages/framework/core/tools/amadeus-intent-autonomy-runtime.ts index aaf2e6a56..36ab392fa 100644 --- a/packages/framework/core/tools/amadeus-intent-autonomy-runtime.ts +++ b/packages/framework/core/tools/amadeus-intent-autonomy-runtime.ts @@ -776,7 +776,7 @@ export interface IntentAutonomyStatusEnvelope { readonly resumeCondition: ResumeCondition | null; readonly legacyStandingGrantCount: number; readonly unreviewedAutoDecisionCount: number; - readonly terminalLiveCompletionCapable: false; + readonly terminalLiveCompletionCapable: true; } export function projectIntentAutonomyStatus(projection: AutonomyProjection): IntentAutonomyStatusEnvelope { @@ -795,6 +795,6 @@ export function projectIntentAutonomyStatus(projection: AutonomyProjection): Int resumeCondition: projection.parkEnvelope?.resumeCondition ?? null, legacyStandingGrantCount: projection.legacyStandingGrantIds.length, unreviewedAutoDecisionCount: projection.autoDecisions.filter((decision) => decision.reviewState === "unreviewed").length, - terminalLiveCompletionCapable: false, + terminalLiveCompletionCapable: true, }; } diff --git a/packages/framework/core/tools/amadeus-intent-completion.ts b/packages/framework/core/tools/amadeus-intent-completion.ts new file mode 100644 index 000000000..76533fedd --- /dev/null +++ b/packages/framework/core/tools/amadeus-intent-completion.ts @@ -0,0 +1,1379 @@ +// Credential-attested five-harness live verification (#2067). +// +// Core owns cohort/receipt verification invariants. Harness adapters own only +// credential authorization and native observation. No GitHub, PR, merge, +// deployment, runner, or supervisor semantics belong here. + +import { + canonicalContractValueDigest, + canonicalTupleDigest, + type ContractErrorCode, + type ContractResult, +} from "./amadeus-autonomy-review.ts"; +import { + assertLegalAutonomyProjection, + type AutonomyProjection, + type IntentGrant, + type WorkflowResult, +} from "./amadeus-intent-autonomy.ts"; +import type { LiveAuthorizationPort } from "./amadeus-loop-monitor-runtime.ts"; +import { + HARNESS_REGISTRY, + type HarnessDescriptor, + type ValidatedHarnessRegistry, +} from "./amadeus-harness-registry.ts"; +import type { ReviewIntentSeed } from "./amadeus-autonomy-review.ts"; + +export type StableId = string; +export type Sha256Digest = string; +export type CompletionHarnessId = HarnessDescriptor["id"]; +export const INTENT_COMPLETION_TRANSACTION_COMMITTED_EVENT = "INTENT_COMPLETION_TRANSACTION_COMMITTED"; +// Live verification is optional evidence. The production workflow completion +// path must never import this module or wait for a receipt cohort. +export const CORE_INTENT_COMPLETION_REQUIRES_LIVE_RECEIPTS = false as const; + +const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,191}$/; +const SHA256 = /^sha256:[0-9a-f]{64}$/; + +function success(value: T): ContractResult { + return { ok: true, value }; +} + +function failure(code: ContractErrorCode, locus: string, detail: string): ContractResult { + return { ok: false, error: { code, locus, detail } }; +} + +function compareUtf8(left: string, right: string): number { + return Buffer.compare(Buffer.from(left, "utf8"), Buffer.from(right, "utf8")); +} + +function digest(schema: string, value: unknown): ContractResult { + return canonicalContractValueDigest(schema, value); +} + +function stableId(prefix: string, domain: string, atoms: readonly { tag: string; value: string | null }[]): StableId { + return `${prefix}-${canonicalTupleDigest(domain, atoms).slice("sha256:".length, "sha256:".length + 32)}`; +} + +function sameRevision(left: LiveScenarioRevision, right: LiveScenarioRevision): boolean { + return left.implementationRevision === right.implementationRevision && + left.packageDigest === right.packageDigest && + left.registryDigest === right.registryDigest && + left.scenarioDigest === right.scenarioDigest; +} + +function validRevision(revision: LiveScenarioRevision): boolean { + return SAFE_ID.test(revision.implementationRevision) && SHA256.test(revision.packageDigest) && + SHA256.test(revision.registryDigest) && SHA256.test(revision.scenarioDigest); +} + +function exactlyKeys(value: Record, expected: readonly string[]): boolean { + const observed = Object.keys(value).sort(compareUtf8); + return observed.length === expected.length && + observed.every((field, index) => field === [...expected].sort(compareUtf8)[index]); +} + +export interface AuditEventPlan { + readonly eventType: string; + readonly eventIdentity: StableId; + readonly intentUuid: StableId; + readonly fields: Readonly>; + readonly payloadDigest: Sha256Digest; +} + +export interface CanonicalAuditEvent extends AuditEventPlan { + readonly transactionId: StableId; + readonly auditRevision: number; +} + +export interface AuditTransaction { + readonly transactionId: StableId; + readonly expectedRevision: number; +} + +export interface AuditCommitReceipt { + readonly transactionId: StableId; + readonly committedEventIdentities: readonly StableId[]; + readonly auditRevision: number; + readonly stateProjectionRevision: number; +} + +export interface RevisionBinding { + readonly implementationRevision: StableId; + readonly packageDigest: Sha256Digest; +} + +export interface LiveScenarioRevision extends RevisionBinding { + readonly registryDigest: Sha256Digest; + readonly scenarioDigest: Sha256Digest; +} + +export interface RequiredCompletionCohort { + readonly schemaVersion: "1"; + readonly cohortId: "intent-autonomy-live"; + readonly harnessIds: readonly CompletionHarnessId[]; + readonly registryDigest: Sha256Digest; + readonly cohortDigest: Sha256Digest; +} + +function registryRows(registry: ValidatedHarnessRegistry): readonly HarnessDescriptor[] { + return [...registry.descriptors].sort((left, right) => compareUtf8(left.id, right.id)); +} + +export function validateHarnessRegistry( + descriptors: readonly HarnessDescriptor[] = HARNESS_REGISTRY, +): ContractResult { + if (descriptors.length === 0) return failure("MALFORMED", "registry", "harness registry is empty"); + const ids = descriptors.map((descriptor) => descriptor.id); + if (ids.some((id) => !SAFE_ID.test(id)) || new Set(ids).size !== ids.length) { + return failure("MALFORMED", "registry", "harness ids must be safe and unique"); + } + const sorted = [...descriptors].sort((left, right) => compareUtf8(left.id, right.id)); + const registryDigest = digest("harness-registry", sorted); + return registryDigest.ok + ? success({ descriptors: sorted, registryDigest: registryDigest.value }) + : registryDigest; +} + +export function resolveRequiredCompletionCohort( + registry: ValidatedHarnessRegistry, +): ContractResult { + if (!SHA256.test(registry.registryDigest)) return failure("MALFORMED", "registryDigest", "invalid registry digest"); + const eligible = registryRows(registry).filter((descriptor) => descriptor.packageFace && + descriptor.autonomyContract && descriptor.autonomyLive && + descriptor.native.liveAuthorization === "credential-attested" && + descriptor.native.judgeReplay === "invoke-once"); + const harnessIds = eligible.map((descriptor) => descriptor.id); + if (harnessIds.length === 0 || new Set(harnessIds).size !== harnessIds.length) { + return failure("MALFORMED", "cohort", "completion cohort must be non-empty and unique"); + } + const cohortDigest = digest("completion-cohort", { + schemaVersion: "1", + cohortId: "intent-autonomy-live", + registryDigest: registry.registryDigest, + harnessIds, + }); + return cohortDigest.ok ? success({ + schemaVersion: "1", + cohortId: "intent-autonomy-live", + harnessIds, + registryDigest: registry.registryDigest, + cohortDigest: cohortDigest.value, + }) : cohortDigest; +} + +export interface IntentLiveExecutionAuthorization { + readonly schemaVersion: "1"; + readonly authorizationId: StableId; + readonly providerAuthorizationId: StableId; + readonly intentUuid: StableId; + readonly harnessId: CompletionHarnessId; + readonly cohortDigest: Sha256Digest; + readonly revision: LiveScenarioRevision; + readonly environmentId: StableId; + readonly issuerPrincipalId: StableId; + readonly actorId: StableId; + readonly traceId: StableId; + readonly spanId: StableId; + readonly attestationDigest: Sha256Digest; + readonly authorizationPayloadDigest: Sha256Digest; +} + +export type IntentLiveAuthorizationEventPlan = AuditEventPlan & { readonly eventType: "LIVE_SMOKE_AUTHORIZED" }; + +export interface CommittedIntentLiveExecutionAuthorization extends IntentLiveExecutionAuthorization { + readonly authorizationEventIdentity: StableId; + readonly commitReceipt: AuditCommitReceipt; +} + +function authorizationPayload( + input: Omit, +): Record { + return { + schemaVersion: input.schemaVersion, + authorizationId: input.authorizationId, + providerAuthorizationId: input.providerAuthorizationId, + intentUuid: input.intentUuid, + harnessId: input.harnessId, + cohortDigest: input.cohortDigest, + implementationRevision: input.revision.implementationRevision, + packageDigest: input.revision.packageDigest, + registryDigest: input.revision.registryDigest, + scenarioDigest: input.revision.scenarioDigest, + environmentId: input.environmentId, + issuerPrincipalId: input.issuerPrincipalId, + actorId: input.actorId, + traceId: input.traceId, + spanId: input.spanId, + attestationDigest: input.attestationDigest, + }; +} + +export interface IntentLiveAuthorizationService { + authorize(input: { + readonly intentUuid: StableId; + readonly harnessId: CompletionHarnessId; + readonly revision: LiveScenarioRevision; + readonly cohort: RequiredCompletionCohort; + readonly registry: ValidatedHarnessRegistry; + }): Promise>; + bindCommit(input: { + readonly authorization: IntentLiveExecutionAuthorization; + readonly audit: readonly [IntentLiveAuthorizationEventPlan]; + readonly receipt: AuditCommitReceipt; + }): ContractResult; +} + +export function createIntentLiveAuthorizationService(deps: { + readonly port: LiveAuthorizationPort; +}): IntentLiveAuthorizationService { + return { + async authorize(input) { + const descriptor = input.registry.descriptors.find((candidate) => candidate.id === input.harnessId); + if (!SAFE_ID.test(input.intentUuid) || !validRevision(input.revision) || + input.revision.registryDigest !== input.registry.registryDigest || + input.cohort.registryDigest !== input.registry.registryDigest || + !input.cohort.harnessIds.includes(input.harnessId) || descriptor === undefined || !descriptor.autonomyLive) { + return failure("MALFORMED", "authorization", "intent, revision, registry, or cohort binding is invalid"); + } + const authorized = await deps.port.authorize({ + intentUuid: input.intentUuid, + monitorId: `intent-completion:${input.harnessId}`, + scopeDigest: input.cohort.cohortDigest, + harnessId: input.harnessId, + ...input.revision, + }); + if (!authorized.authorized) return failure("PROVENANCE_REQUIRED", "credential", authorized.reason); + const safeValues = [authorized.authorizationId, authorized.actorId, authorized.environmentId, + authorized.issuerPrincipalId, authorized.traceId, authorized.spanId]; + if (safeValues.some((value) => value === undefined || !SAFE_ID.test(value)) || + authorized.attestationDigest === undefined || !SHA256.test(authorized.attestationDigest)) { + return failure("PROVENANCE_REQUIRED", "attestation", "credential-attested authorization metadata is incomplete"); + } + const authorizationId = stableId("live-authorization", "amadeus.live-authorization.v1", [ + { tag: "provider-authorization", value: authorized.authorizationId }, + { tag: "intent", value: input.intentUuid }, + { tag: "harness", value: input.harnessId }, + { tag: "cohort", value: input.cohort.cohortDigest }, + { tag: "implementation", value: input.revision.implementationRevision }, + { tag: "package", value: input.revision.packageDigest }, + { tag: "registry", value: input.revision.registryDigest }, + { tag: "scenario", value: input.revision.scenarioDigest }, + { tag: "environment", value: authorized.environmentId as string }, + { tag: "attestation", value: authorized.attestationDigest }, + ]); + const base = { + schemaVersion: "1" as const, + authorizationId, + providerAuthorizationId: authorized.authorizationId, + intentUuid: input.intentUuid, + harnessId: input.harnessId, + cohortDigest: input.cohort.cohortDigest, + revision: { ...input.revision }, + environmentId: authorized.environmentId as string, + issuerPrincipalId: authorized.issuerPrincipalId as string, + actorId: authorized.actorId, + traceId: authorized.traceId as string, + spanId: authorized.spanId as string, + attestationDigest: authorized.attestationDigest, + }; + const payloadDigest = digest("live-authorization-payload", authorizationPayload(base)); + if (!payloadDigest.ok) return payloadDigest; + const authorization: IntentLiveExecutionAuthorization = { + ...base, + authorizationPayloadDigest: payloadDigest.value, + }; + const eventIdentity = stableId("live-authorization-event", "amadeus.live-authorization-event.v1", [ + { tag: "authorization", value: authorization.authorizationId }, + { tag: "payload", value: authorization.authorizationPayloadDigest }, + ]); + return success({ authorization, audit: [{ + eventType: "LIVE_SMOKE_AUTHORIZED", + eventIdentity, + intentUuid: input.intentUuid, + fields: { payload_v1: JSON.stringify({ ...authorizationPayload(base), authorizationPayloadDigest: payloadDigest.value }) }, + payloadDigest: payloadDigest.value, + }] }); + }, + bindCommit(input) { + const event = input.audit[0]; + if (event.intentUuid !== input.authorization.intentUuid || + !input.receipt.committedEventIdentities.includes(event.eventIdentity) || + !SAFE_ID.test(input.receipt.transactionId)) { + return failure("PROVENANCE_REQUIRED", "authorizationCommit", "authorization event is not committed"); + } + return success({ + ...input.authorization, + authorizationEventIdentity: event.eventIdentity, + commitReceipt: input.receipt, + }); + }, + }; +} + +export interface LiveObservation { + readonly judgeInvocationId: StableId; + readonly judgeObserved: true; + readonly electionDecisionId: StableId; + readonly electionOutcome: "elected" | "loud-degradation"; + readonly degradationReason: StableId | null; +} + +export interface RawIntentLiveReceipt { + readonly schemaVersion: "1"; + readonly receiptId: StableId; + readonly intentUuid: StableId; + readonly harnessId: CompletionHarnessId; + readonly authorizationId: StableId; + readonly authorizationEventIdentity: StableId; + readonly authorizationCommitTransactionId: StableId; + readonly revision: LiveScenarioRevision; + readonly environmentId: StableId; + readonly traceId: StableId; + readonly spanId: StableId; + readonly attestationDigest: Sha256Digest; + readonly outcome: "passed" | "skipped" | "failed"; + readonly observation: LiveObservation | null; +} + +export interface IntentLiveRunReservation { + readonly schemaVersion: "1"; + readonly runId: StableId; + readonly operationReference: StableId; + readonly intentUuid: StableId; + readonly harnessId: CompletionHarnessId; + readonly authorizationId: StableId; + readonly judgeInvocationId: StableId; + readonly revision: LiveScenarioRevision; + readonly maxDispatches: 2; + readonly dispatchesAuthorized: 1 | 2; + readonly status: "reserved" | "started" | "redispatch-authorized" | "dispatch-claimed" | "completed" | "incomplete"; +} + +export type IntentLiveRunStateEventPlan = AuditEventPlan & { + readonly eventType: + | "LIVE_SMOKE_RUN_RESERVED" + | "LIVE_SMOKE_RUN_STARTED" + | "LIVE_SMOKE_RUN_REDISPATCH_AUTHORIZED" + | "LIVE_SMOKE_RUN_DISPATCH_CLAIMED" + | "LIVE_SMOKE_RUN_COMPLETED" + | "LIVE_SMOKE_RUN_INCOMPLETE"; +}; + +export type NativeRunReconciliation = + | { readonly kind: "completed"; readonly receipt: RawIntentLiveReceipt; readonly proofDigest: Sha256Digest } + | { readonly kind: "attested-no-effect"; readonly proofDigest: Sha256Digest } + | { readonly kind: "effect-possible" | "unknown"; readonly proofDigest: Sha256Digest | null }; + +export interface CommittedIntentLiveRunState extends IntentLiveRunReservation { + readonly stateEventIdentity: StableId; + readonly stateCommitReceipt: AuditCommitReceipt; + readonly sourceAuditRevision: number; + readonly sourceStateProjectionRevision: number; +} + +export interface CanonicalLiveRunSnapshot { + readonly intentUuid: StableId; + readonly auditRevision: number; + readonly stateProjectionRevision: number; + readonly authorization: CommittedIntentLiveExecutionAuthorization; + readonly run: CommittedIntentLiveRunState; +} + +export interface CanonicalLiveRunStateReader { + readRunSnapshot(input: { + readonly intentUuid: StableId; + readonly runId: StableId; + }): ContractResult; +} + +export interface AttestedNoEffectProofVerifier { + verify(input: { + readonly snapshot: CanonicalLiveRunSnapshot; + readonly reconciliation: Extract; + }): ContractResult<{ readonly proofDigest: Sha256Digest }>; +} + +const RUN_DISPATCH_PERMIT = Symbol("RunDispatchPermit"); +const CLAIMED_RUN_DISPATCH = Symbol("ClaimedRunDispatch"); + +export interface RunDispatchPermit { + readonly schemaVersion: "1"; + readonly permitId: StableId; + readonly runId: StableId; + readonly intentUuid: StableId; + readonly authorizationId: StableId; + readonly operationReference: StableId; + readonly judgeInvocationId: StableId; + readonly attempt: 1 | 2; + readonly authorizedStateEventIdentity: StableId; + readonly authorizedCommitTransactionId: StableId; + readonly sourceAuditRevision: number; + readonly permitDigest: Sha256Digest; + readonly [RUN_DISPATCH_PERMIT]: true; +} + +export interface ClaimedRunDispatch extends RunDispatchPermit { + readonly claimEventIdentity: StableId; + readonly claimCommitTransactionId: StableId; + readonly claimAuditRevision: number; + readonly [CLAIMED_RUN_DISPATCH]: true; +} + +export interface NativeDispatchReceipt { + readonly schemaVersion: "1"; + readonly dispatchKeyDigest: Sha256Digest; + readonly nativeOperationId: StableId; + readonly outcome: "started" | "attached"; + readonly proofDigest: Sha256Digest; +} + +export interface IntentNativeRunPort { + reconcile(input: { + readonly operationReference: StableId; + readonly judgeInvocationId: StableId; + }): Promise>; + dispatch(claimed: ClaimedRunDispatch): Promise>; +} + +function runStateEvent( + run: IntentLiveRunReservation, + previousEventIdentity: StableId | null, + proofDigest: Sha256Digest | null, +): ContractResult { + const eventTypeByStatus = { + reserved: "LIVE_SMOKE_RUN_RESERVED", + started: "LIVE_SMOKE_RUN_STARTED", + "redispatch-authorized": "LIVE_SMOKE_RUN_REDISPATCH_AUTHORIZED", + "dispatch-claimed": "LIVE_SMOKE_RUN_DISPATCH_CLAIMED", + completed: "LIVE_SMOKE_RUN_COMPLETED", + incomplete: "LIVE_SMOKE_RUN_INCOMPLETE", + } as const; + const payload = { ...run, previousEventIdentity, proofDigest }; + const payloadDigest = digest("intent-live-run-state", payload); + if (!payloadDigest.ok) return payloadDigest; + return success({ + eventType: eventTypeByStatus[run.status], + eventIdentity: stableId("intent-live-run-event", "amadeus.intent-live-run-event.v1", [ + { tag: "run", value: run.runId }, + { tag: "status", value: run.status }, + { tag: "dispatches", value: String(run.dispatchesAuthorized) }, + { tag: "previous", value: previousEventIdentity }, + { tag: "payload", value: payloadDigest.value }, + ]), + intentUuid: run.intentUuid, + fields: { payload_v1: JSON.stringify(payload) }, + payloadDigest: payloadDigest.value, + }); +} + +function bindRunCommit( + planned: IntentLiveRunReservation, + audit: readonly [IntentLiveRunStateEventPlan], + receipt: AuditCommitReceipt, +): ContractResult { + if (!receipt.committedEventIdentities.includes(audit[0].eventIdentity)) { + return failure("PROVENANCE_REQUIRED", "runStateCommit", "run state event is not committed"); + } + return success({ + ...planned, + stateEventIdentity: audit[0].eventIdentity, + stateCommitReceipt: receipt, + sourceAuditRevision: receipt.auditRevision, + sourceStateProjectionRevision: receipt.stateProjectionRevision, + }); +} + +function dispatchPermit(state: CommittedIntentLiveRunState): ContractResult { + if (state.status !== "started" && state.status !== "redispatch-authorized") return success(null); + const attempt: 1 | 2 = state.status === "started" ? 1 : 2; + const permitValue = { + schemaVersion: "1" as const, + runId: state.runId, + intentUuid: state.intentUuid, + authorizationId: state.authorizationId, + operationReference: state.operationReference, + judgeInvocationId: state.judgeInvocationId, + attempt, + authorizedStateEventIdentity: state.stateEventIdentity, + authorizedCommitTransactionId: state.stateCommitReceipt.transactionId, + sourceAuditRevision: state.sourceAuditRevision, + }; + const permitDigest = digest("intent-live-run-dispatch-permit", permitValue); + if (!permitDigest.ok) return permitDigest; + return success({ + ...permitValue, + permitId: stableId("run-dispatch-permit", "amadeus.intent-live-run-dispatch-permit.v1", [ + { tag: "run", value: state.runId }, + { tag: "attempt", value: String(attempt) }, + { tag: "state-event", value: state.stateEventIdentity }, + { tag: "commit", value: state.stateCommitReceipt.transactionId }, + { tag: "digest", value: permitDigest.value }, + ]), + permitDigest: permitDigest.value, + [RUN_DISPATCH_PERMIT]: true, + }); +} + +function nextRunState( + snapshot: CanonicalLiveRunSnapshot, + reconciliation: NativeRunReconciliation, + proofVerifier: AttestedNoEffectProofVerifier, +): ContractResult<{ readonly next: IntentLiveRunReservation; readonly receipt: RawIntentLiveReceipt | null; readonly proofDigest: Sha256Digest | null }> { + const run = snapshot.run; + if (reconciliation.kind === "completed") { + return success({ next: { ...run, status: "completed" }, receipt: reconciliation.receipt, + proofDigest: reconciliation.proofDigest }); + } + if (reconciliation.kind !== "attested-no-effect") { + return success({ next: { ...run, status: "incomplete" }, receipt: null, + proofDigest: reconciliation.proofDigest }); + } + const proof = proofVerifier.verify({ snapshot, reconciliation }); + if (!proof.ok) return proof; + if (run.status === "reserved") { + return success({ next: { ...run, status: "started", dispatchesAuthorized: 1 }, receipt: null, + proofDigest: proof.value.proofDigest }); + } + if (run.dispatchesAuthorized === 1 && (run.status === "started" || run.status === "dispatch-claimed")) { + return success({ next: { ...run, status: "redispatch-authorized", dispatchesAuthorized: 2 }, receipt: null, + proofDigest: proof.value.proofDigest }); + } + return success({ next: { ...run, status: "incomplete" }, receipt: null, proofDigest: proof.value.proofDigest }); +} + +export interface IntentLiveRunCoordinator { + reserve(input: { readonly authorization: CommittedIntentLiveExecutionAuthorization }): ContractResult<{ + readonly reservation: IntentLiveRunReservation; + readonly audit: readonly [IntentLiveRunStateEventPlan]; + }>; + bindStateCommit(input: { + readonly planned: IntentLiveRunReservation; + readonly audit: readonly [IntentLiveRunStateEventPlan]; + readonly receipt: AuditCommitReceipt; + }): ContractResult; + planNext(input: { readonly intentUuid: StableId; readonly runId: StableId }): Promise>; + bindTransitionCommit(input: { + readonly prior: CommittedIntentLiveRunState; + readonly next: IntentLiveRunReservation; + readonly audit: readonly [IntentLiveRunStateEventPlan]; + readonly receipt: AuditCommitReceipt; + }): ContractResult<{ readonly state: CommittedIntentLiveRunState; readonly dispatchPermit: RunDispatchPermit | null }>; + claimDispatch(permit: RunDispatchPermit): ContractResult<{ + readonly prior: CommittedIntentLiveRunState; + readonly claimed: IntentLiveRunReservation; + readonly audit: readonly [IntentLiveRunStateEventPlan]; + }>; + bindDispatchClaimCommit(input: { + readonly permit: RunDispatchPermit; + readonly prior: CommittedIntentLiveRunState; + readonly claimed: IntentLiveRunReservation; + readonly audit: readonly [IntentLiveRunStateEventPlan]; + readonly receipt: AuditCommitReceipt; + }): ContractResult; + dispatch(claimed: ClaimedRunDispatch): Promise>; +} + +export function createIntentLiveRunCoordinator(deps: { + readonly runReader: CanonicalLiveRunStateReader; + readonly proofVerifier: AttestedNoEffectProofVerifier; + readonly nativePort: IntentNativeRunPort; +}): IntentLiveRunCoordinator { + return { + reserve({ authorization }) { + const identityAtoms = [ + { tag: "intent", value: authorization.intentUuid }, + { tag: "harness", value: authorization.harnessId }, + { tag: "authorization", value: authorization.authorizationId }, + { tag: "scenario", value: authorization.revision.scenarioDigest }, + ]; + const reservation: IntentLiveRunReservation = { + schemaVersion: "1", + runId: stableId("intent-live-run", "amadeus.intent-live-run.v1", identityAtoms), + operationReference: stableId("intent-live-operation", "amadeus.intent-live-operation.v1", identityAtoms), + intentUuid: authorization.intentUuid, + harnessId: authorization.harnessId, + authorizationId: authorization.authorizationId, + judgeInvocationId: stableId("intent-live-judge", "amadeus.intent-live-judge.v1", identityAtoms), + revision: authorization.revision, + maxDispatches: 2, + dispatchesAuthorized: 1, + status: "reserved", + }; + const audit = runStateEvent(reservation, null, null); + return audit.ok ? success({ reservation, audit: [audit.value] }) : audit; + }, + bindStateCommit(input) { + return bindRunCommit(input.planned, input.audit, input.receipt); + }, + async planNext(input) { + const snapshot = deps.runReader.readRunSnapshot(input); + if (!snapshot.ok) return snapshot; + const reconciliation = await deps.nativePort.reconcile({ + operationReference: snapshot.value.run.operationReference, + judgeInvocationId: snapshot.value.run.judgeInvocationId, + }); + if (!reconciliation.ok) return reconciliation; + const planned = nextRunState(snapshot.value, reconciliation.value, deps.proofVerifier); + if (!planned.ok) return planned; + const audit = runStateEvent(planned.value.next, snapshot.value.run.stateEventIdentity, planned.value.proofDigest); + return audit.ok ? success({ + prior: snapshot.value.run, + next: planned.value.next, + audit: [audit.value], + receipt: planned.value.receipt, + }) : audit; + }, + bindTransitionCommit(input) { + if (input.prior.runId !== input.next.runId || input.prior.stateEventIdentity === input.audit[0].eventIdentity) { + return failure("CONFLICT", "runTransition", "run transition does not advance the canonical head"); + } + const state = bindRunCommit(input.next, input.audit, input.receipt); + if (!state.ok) return state; + const permit = dispatchPermit(state.value); + return permit.ok ? success({ state: state.value, dispatchPermit: permit.value }) : permit; + }, + claimDispatch(permit) { + if (permit[RUN_DISPATCH_PERMIT] !== true) return failure("PROVENANCE_REQUIRED", "dispatchPermit", "unbranded permit"); + const snapshot = deps.runReader.readRunSnapshot({ intentUuid: permit.intentUuid, runId: permit.runId }); + if (!snapshot.ok) return snapshot; + const prior = snapshot.value.run; + const expectedStatus = permit.attempt === 1 ? "started" : "redispatch-authorized"; + if (prior.stateEventIdentity !== permit.authorizedStateEventIdentity || prior.status !== expectedStatus || + prior.authorizationId !== permit.authorizationId || prior.operationReference !== permit.operationReference) { + return failure("CONFLICT", "dispatchClaim", "permit is stale, forged, or already claimed"); + } + const claimed: IntentLiveRunReservation = { ...prior, status: "dispatch-claimed" }; + const audit = runStateEvent(claimed, prior.stateEventIdentity, permit.permitDigest); + return audit.ok ? success({ prior, claimed, audit: [audit.value] }) : audit; + }, + bindDispatchClaimCommit(input) { + const state = bindRunCommit(input.claimed, input.audit, input.receipt); + if (!state.ok) return state; + if (state.value.status !== "dispatch-claimed") { + return failure("CONFLICT", "dispatchClaim", "claim event did not commit dispatch-claimed state"); + } + return success({ + ...input.permit, + claimEventIdentity: state.value.stateEventIdentity, + claimCommitTransactionId: state.value.stateCommitReceipt.transactionId, + claimAuditRevision: state.value.sourceAuditRevision, + [CLAIMED_RUN_DISPATCH]: true, + }); + }, + async dispatch(claimed) { + if (claimed[CLAIMED_RUN_DISPATCH] !== true) { + return failure("PROVENANCE_REQUIRED", "claimedDispatch", "dispatch requires committed claim"); + } + const snapshot = deps.runReader.readRunSnapshot({ intentUuid: claimed.intentUuid, runId: claimed.runId }); + if (!snapshot.ok) return snapshot; + if (snapshot.value.run.stateEventIdentity !== claimed.claimEventIdentity || + snapshot.value.run.status !== "dispatch-claimed") { + return failure("CONFLICT", "claimedDispatch", "claim is not the canonical run head"); + } + const result = await deps.nativePort.dispatch(claimed); + if (!result.ok) return result; + const expectedKey = digest("intent-live-native-dispatch", { + operationReference: claimed.operationReference, + attempt: claimed.attempt, + }); + if (!expectedKey.ok) return expectedKey; + return result.value.dispatchKeyDigest === expectedKey.value && SAFE_ID.test(result.value.nativeOperationId) && + SHA256.test(result.value.proofDigest) + ? result + : failure("CONFLICT", "nativeDispatchReceipt", "native idempotency receipt does not match claim"); + }, + }; +} + +export interface CanonicalLiveAuthorizationSnapshot { + readonly intentUuid: StableId; + readonly audit: readonly CanonicalAuditEvent[]; + readonly auditRevision: number; + readonly stateProjectionRevision: number; + readonly authorization: CommittedIntentLiveExecutionAuthorization; +} + +export interface CanonicalLiveEvidenceReader { + readAuthorizationSnapshot(input: { + readonly intentUuid: StableId; + readonly authorizationId: StableId; + }): ContractResult; +} + +export interface ValidatedIntentLiveReceipt extends RawIntentLiveReceipt { + readonly outcome: "passed"; + readonly observation: LiveObservation; + readonly observationProofDigest: Sha256Digest; + readonly validationDigest: Sha256Digest; + readonly sourceAuditRevision: number; +} + +export type LiveReceiptValidatedEventPlan = AuditEventPlan & { readonly eventType: "LIVE_SMOKE_RECEIPT_VALIDATED" }; + +export interface PlannedValidatedIntentLiveReceipt { + readonly receipt: ValidatedIntentLiveReceipt; + readonly audit: readonly [LiveReceiptValidatedEventPlan]; +} + +export interface CommittedValidatedIntentLiveReceipt extends ValidatedIntentLiveReceipt { + readonly validationEventIdentity: StableId; + readonly validationCommitReceipt: AuditCommitReceipt; +} + +function observedEvent(snapshot: CanonicalLiveAuthorizationSnapshot, type: string, identity: string, traceId: string): boolean { + return snapshot.audit.some((event) => event.eventType === type && event.eventIdentity === identity && + event.fields.trace_id === traceId); +} + +function matchingReceiptAuthorization( + receipt: RawIntentLiveReceipt, + snapshot: CanonicalLiveAuthorizationSnapshot, +): boolean { + const authorization = snapshot.authorization; + return snapshot.intentUuid === receipt.intentUuid && authorization.intentUuid === receipt.intentUuid && + authorization.authorizationId === receipt.authorizationId && + authorization.authorizationEventIdentity === receipt.authorizationEventIdentity && + authorization.commitReceipt.transactionId === receipt.authorizationCommitTransactionId && + authorization.harnessId === receipt.harnessId && sameRevision(authorization.revision, receipt.revision) && + authorization.environmentId === receipt.environmentId && authorization.traceId === receipt.traceId && + authorization.spanId === receipt.spanId && authorization.attestationDigest === receipt.attestationDigest; +} + +function validObservationShape(observation: LiveObservation): boolean { + return (observation.electionOutcome === "elected" && observation.degradationReason === null) || + (observation.electionOutcome === "loud-degradation" && observation.degradationReason !== null && + SAFE_ID.test(observation.degradationReason)); +} + +function canonicalObservationExists( + snapshot: CanonicalLiveAuthorizationSnapshot, + receipt: RawIntentLiveReceipt & { readonly observation: LiveObservation }, +): boolean { + const observation = receipt.observation; + const decisionType = observation.electionOutcome === "elected" ? "AUTO_DECIDED" : "AUTO_DECISION_DEGRADED"; + return observedEvent(snapshot, "LOOP_JUDGE_STARTED", observation.judgeInvocationId, receipt.traceId) && + observedEvent(snapshot, "LOOP_JUDGE_RESULT_OBSERVED", observation.judgeInvocationId, receipt.traceId) && + observedEvent(snapshot, decisionType, observation.electionDecisionId, receipt.traceId); +} + +export interface IntentLiveReceiptValidator { + validate(receipt: RawIntentLiveReceipt): ContractResult; + bindCommit(input: { + readonly planned: PlannedValidatedIntentLiveReceipt; + readonly receipt: AuditCommitReceipt; + }): ContractResult; +} + +export function createIntentLiveReceiptValidator(deps: { + readonly evidenceReader: CanonicalLiveEvidenceReader; +}): IntentLiveReceiptValidator { + return { + validate(receipt) { + if (receipt.outcome !== "passed" || receipt.observation === null || !receipt.observation.judgeObserved) { + return failure("PROVENANCE_REQUIRED", "liveReceipt", "only an observed passed receipt can be validated"); + } + const snapshot = deps.evidenceReader.readAuthorizationSnapshot({ + intentUuid: receipt.intentUuid, + authorizationId: receipt.authorizationId, + }); + if (!snapshot.ok) return snapshot; + if (!matchingReceiptAuthorization(receipt, snapshot.value)) { + return failure("CONFLICT", "authorization", "receipt does not match canonical authorization"); + } + const observation = receipt.observation; + const passedReceipt = { ...receipt, outcome: "passed" as const, observation }; + if (!validObservationShape(observation) || !canonicalObservationExists(snapshot.value, passedReceipt)) { + return failure("PROVENANCE_REQUIRED", "observation", "Judge and decision observation proof is incomplete"); + } + const proof = digest("live-observation-proof", { + judgeInvocationId: observation.judgeInvocationId, + electionDecisionId: observation.electionDecisionId, + electionOutcome: observation.electionOutcome, + degradationReason: observation.degradationReason, + traceId: receipt.traceId, + sourceAuditRevision: snapshot.value.auditRevision, + }); + if (!proof.ok) return proof; + const validation = digest("validated-live-receipt", { + receipt, + authorizationPayloadDigest: snapshot.value.authorization.authorizationPayloadDigest, + observationProofDigest: proof.value, + sourceAuditRevision: snapshot.value.auditRevision, + }); + if (!validation.ok) return validation; + const validated: ValidatedIntentLiveReceipt = { + ...receipt, + outcome: "passed", + observation, + observationProofDigest: proof.value, + validationDigest: validation.value, + sourceAuditRevision: snapshot.value.auditRevision, + }; + const eventIdentity = stableId("live-receipt-validated", "amadeus.validated-live-receipt-event.v1", [ + { tag: "receipt", value: receipt.receiptId }, + { tag: "validation", value: validation.value }, + ]); + return success({ receipt: validated, audit: [{ + eventType: "LIVE_SMOKE_RECEIPT_VALIDATED", + eventIdentity, + intentUuid: receipt.intentUuid, + fields: { payload_v1: JSON.stringify(validated) }, + payloadDigest: validation.value, + }] }); + }, + bindCommit(input) { + const event = input.planned.audit[0]; + if (!input.receipt.committedEventIdentities.includes(event.eventIdentity)) { + return failure("PROVENANCE_REQUIRED", "validationCommit", "validation event is not committed"); + } + return success({ + ...input.planned.receipt, + validationEventIdentity: event.eventIdentity, + validationCommitReceipt: input.receipt, + }); + }, + }; +} + +export interface CanonicalValidatedReceiptSetSnapshot { + readonly intentUuid: StableId; + readonly auditRevision: number; + readonly stateProjectionRevision: number; + readonly receipts: readonly CommittedValidatedIntentLiveReceipt[]; +} + +export interface CanonicalValidatedReceiptReader { + readValidationSet(input: { + readonly intentUuid: StableId; + readonly validationEventIdentities: readonly StableId[]; + }): ContractResult; +} + +export interface IntentCompletionEvidence { + readonly schemaVersion: "1"; + readonly evidenceId: StableId; + readonly intentUuid: StableId; + readonly cohort: RequiredCompletionCohort; + readonly revision: LiveScenarioRevision; + readonly receiptIds: readonly StableId[]; + readonly authorizationIds: readonly StableId[]; + readonly validationEventIdentities: readonly StableId[]; + readonly validationDigests: readonly Sha256Digest[]; + readonly observationProofDigests: readonly Sha256Digest[]; + readonly sourceAuditRevisions: readonly number[]; + readonly evidenceDigest: Sha256Digest; +} + +export type CompletionEvidenceValidatedEventPlan = AuditEventPlan & { + readonly eventType: "LIVE_COMPLETION_EVIDENCE_VALIDATED"; +}; + +export type IntentCompletionCheck = + | { readonly kind: "complete"; readonly evidence: IntentCompletionEvidence } + | { + readonly kind: "incomplete"; + readonly intentUuid: StableId; + readonly cohort: RequiredCompletionCohort; + readonly revision: LiveScenarioRevision; + readonly missingHarnessIds: readonly CompletionHarnessId[]; + readonly rejectedReceiptIds: readonly StableId[]; + }; + +export type IntentCompletionEvaluation = + | { + readonly check: Extract; + readonly audit: readonly [CompletionEvidenceValidatedEventPlan]; + readonly sourceAuditRevision: number; + readonly sourceStateProjectionRevision: number; + } + | { + readonly check: Extract; + readonly audit: readonly []; + readonly sourceAuditRevision: number; + readonly sourceStateProjectionRevision: number; + }; + +function receiptMatchesTarget( + receipt: CommittedValidatedIntentLiveReceipt, + intentUuid: string, + cohort: RequiredCompletionCohort, + revision: LiveScenarioRevision, +): boolean { + return receipt.intentUuid === intentUuid && cohort.harnessIds.includes(receipt.harnessId) && + sameRevision(receipt.revision, revision) && receipt.outcome === "passed" && + receipt.validationCommitReceipt.committedEventIdentities.includes(receipt.validationEventIdentity); +} + +interface ClassifiedReceiptSet { + readonly byHarness: ReadonlyMap; + readonly missing: readonly CompletionHarnessId[]; + readonly rejected: readonly StableId[]; +} + +function classifyReceiptSet( + receipts: readonly CommittedValidatedIntentLiveReceipt[], + intentUuid: string, + cohort: RequiredCompletionCohort, + revision: LiveScenarioRevision, +): ClassifiedReceiptSet { + const byHarness = new Map(); + const rejected: StableId[] = []; + for (const receipt of receipts) { + if (!receiptMatchesTarget(receipt, intentUuid, cohort, revision)) { + rejected.push(receipt.receiptId); + continue; + } + const values = byHarness.get(receipt.harnessId) ?? []; + values.push(receipt); + byHarness.set(receipt.harnessId, values); + } + const missing = cohort.harnessIds.filter((harnessId) => (byHarness.get(harnessId)?.length ?? 0) === 0); + const duplicates = [...byHarness.values()] + .filter((values) => values.length > 1) + .flatMap((values) => values.map((receipt) => receipt.receiptId)); + return { byHarness, missing, rejected: [...new Set([...rejected, ...duplicates])].sort(compareUtf8) }; +} + +export interface IntentCompletionEvaluator { + evaluate(input: { + readonly intentUuid: StableId; + readonly cohort: RequiredCompletionCohort; + readonly revision: LiveScenarioRevision; + readonly validationEventIdentities: readonly StableId[]; + }): ContractResult; +} + +export function createIntentCompletionEvaluator(deps: { + readonly receiptReader: CanonicalValidatedReceiptReader; +}): IntentCompletionEvaluator { + return { + evaluate(input) { + if (!SAFE_ID.test(input.intentUuid) || !validRevision(input.revision) || + input.revision.registryDigest !== input.cohort.registryDigest) { + return failure("MALFORMED", "completionInput", "invalid completion binding"); + } + const snapshot = deps.receiptReader.readValidationSet({ + intentUuid: input.intentUuid, + validationEventIdentities: input.validationEventIdentities, + }); + if (!snapshot.ok) return snapshot; + const classified = classifyReceiptSet(snapshot.value.receipts, input.intentUuid, input.cohort, input.revision); + if (classified.missing.length > 0 || classified.rejected.length > 0) return success({ + check: { + kind: "incomplete", + intentUuid: input.intentUuid, + cohort: input.cohort, + revision: input.revision, + missingHarnessIds: classified.missing, + rejectedReceiptIds: classified.rejected, + }, + audit: [], + sourceAuditRevision: snapshot.value.auditRevision, + sourceStateProjectionRevision: snapshot.value.stateProjectionRevision, + }); + const ordered = input.cohort.harnessIds.map( + (harnessId) => classified.byHarness.get(harnessId)?.[0] as CommittedValidatedIntentLiveReceipt, + ); + const evidenceBase = { + schemaVersion: "1" as const, + intentUuid: input.intentUuid, + cohort: input.cohort, + revision: input.revision, + receiptIds: ordered.map((receipt) => receipt.receiptId), + authorizationIds: ordered.map((receipt) => receipt.authorizationId), + validationEventIdentities: ordered.map((receipt) => receipt.validationEventIdentity), + validationDigests: ordered.map((receipt) => receipt.validationDigest), + observationProofDigests: ordered.map((receipt) => receipt.observationProofDigest), + sourceAuditRevisions: ordered.map((receipt) => receipt.sourceAuditRevision), + }; + const evidenceDigest = digest("intent-completion-evidence", evidenceBase); + if (!evidenceDigest.ok) return evidenceDigest; + const evidenceId = stableId("intent-completion-evidence", "amadeus.intent-completion-evidence.v1", [ + { tag: "intent", value: input.intentUuid }, + { tag: "cohort", value: input.cohort.cohortDigest }, + { tag: "evidence", value: evidenceDigest.value }, + ]); + const evidence: IntentCompletionEvidence = { ...evidenceBase, evidenceId, evidenceDigest: evidenceDigest.value }; + const payload = { + schemaVersion: "1", + evidenceId, + intentUuid: input.intentUuid, + cohortId: input.cohort.cohortId, + cohortDigest: input.cohort.cohortDigest, + implementationRevision: input.revision.implementationRevision, + packageDigest: input.revision.packageDigest, + registryDigest: input.revision.registryDigest, + scenarioDigest: input.revision.scenarioDigest, + harnessIds: input.cohort.harnessIds, + receiptIds: evidence.receiptIds, + authorizationIds: evidence.authorizationIds, + validationEventIdentities: evidence.validationEventIdentities, + validationDigests: evidence.validationDigests, + observationProofDigests: evidence.observationProofDigests, + sourceAuditRevisions: evidence.sourceAuditRevisions, + evidenceDigest: evidence.evidenceDigest, + sourceAuditRevision: snapshot.value.auditRevision, + }; + const payloadDigest = digest("completion-evidence-validated-payload", payload); + if (!payloadDigest.ok) return payloadDigest; + const eventIdentity = stableId("completion-evidence-validated", "amadeus.completion-evidence-validated-event.v1", [ + { tag: "intent", value: input.intentUuid }, + { tag: "evidence", value: evidence.evidenceId }, + { tag: "cohort", value: input.cohort.cohortDigest }, + { tag: "source-revision", value: String(snapshot.value.auditRevision) }, + { tag: "payload", value: payloadDigest.value }, + ]); + return success({ + check: { kind: "complete", evidence }, + audit: [{ + eventType: "LIVE_COMPLETION_EVIDENCE_VALIDATED", + eventIdentity, + intentUuid: input.intentUuid, + fields: { payload_v1: JSON.stringify(payload) }, + payloadDigest: payloadDigest.value, + }], + sourceAuditRevision: snapshot.value.auditRevision, + sourceStateProjectionRevision: snapshot.value.stateProjectionRevision, + }); + }, + }; +} + +export interface TerminalCommitPlan { + readonly transaction: AuditTransaction; + readonly orderedEvents: readonly AuditEventPlan[]; + readonly completionEvidence: IntentCompletionEvidence; + readonly expectedEventIdentities: readonly StableId[]; + readonly expectedStateProjectionRevision: number; + readonly terminalProjection: AutonomyProjection; + readonly completionSealDigest: Sha256Digest; + readonly result: WorkflowResult & { readonly outcome: "completed" }; +} + +export interface TerminalCommitReceipt { + readonly evidenceId: StableId; + readonly transactionId: StableId; + readonly committedEventIdentities: readonly StableId[]; + readonly stateProjectionRevision: number; + readonly completionSealDigest: Sha256Digest; + readonly terminalProjection: AutonomyProjection; + readonly result: WorkflowResult & { readonly outcome: "completed" }; +} + +export function terminalTransactionAuditFields(plan: TerminalCommitPlan): Record { + return { + "Intent Uuid": plan.completionEvidence.intentUuid, + "Transaction Id": plan.transaction.transactionId, + "Evidence Id": plan.completionEvidence.evidenceId, + "Evidence Digest": plan.completionEvidence.evidenceDigest, + "Completion Seal Digest": plan.completionSealDigest, + Transaction: JSON.stringify({ + eventType: "INTENT_COMPLETION_TRANSACTION_COMMITTED", + transaction: plan.transaction, + expectedEventIdentities: plan.expectedEventIdentities, + expectedStateProjectionRevision: plan.expectedStateProjectionRevision, + }), + }; +} + +function terminalEvent(eventType: string, intentUuid: string, payload: unknown): ContractResult { + const payloadDigest = digest(`intent-terminal-${eventType.toLowerCase()}`, payload); + if (!payloadDigest.ok) return payloadDigest; + return success({ + eventType, + eventIdentity: stableId("intent-terminal-event", "amadeus.intent-terminal-event.v1", [ + { tag: "type", value: eventType }, + { tag: "intent", value: intentUuid }, + { tag: "payload", value: payloadDigest.value }, + ]), + intentUuid, + fields: { payload_v1: JSON.stringify(payload) }, + payloadDigest: payloadDigest.value, + }); +} + +function completedGrant(current: AutonomyProjection): IntentGrant | null { + return current.currentGrant === null ? null : { ...current.currentGrant, state: "completed" }; +} + +function canPlanTerminalCommit(input: { + readonly current: AutonomyProjection; + readonly evaluation: Extract; +}): boolean { + const evidence = input.evaluation.check.evidence; + return input.current.intentUuid === evidence.intentUuid && + input.current.workflowExecutionState === "running" && + input.current.pendingExercise === null && + input.current.parkEnvelope === null && + Number.isSafeInteger(input.evaluation.sourceAuditRevision) && + Number.isSafeInteger(input.evaluation.sourceStateProjectionRevision) && + input.evaluation.sourceStateProjectionRevision !== Number.MAX_SAFE_INTEGER; +} + +export function planTerminalCommit(input: { + readonly current: AutonomyProjection; + readonly evaluation: Extract; +}): ContractResult { + try { + assertLegalAutonomyProjection(input.current); + } catch (error) { + return failure("ILLEGAL_STATE", "autonomy", error instanceof Error ? error.message : String(error)); + } + const evidence = input.evaluation.check.evidence; + if (!canPlanTerminalCommit(input)) { + return failure("ILLEGAL_STATE", "terminalPlan", "only a stable running complete evaluation can terminate"); + } + const grant = completedGrant(input.current); + const terminalProjection: AutonomyProjection = { + ...input.current, + workflowExecutionState: null, + currentGrant: null, + terminalGrantHistory: grant === null + ? input.current.terminalGrantHistory + : [...input.current.terminalGrantHistory, grant], + pendingExercise: null, + parkEnvelope: null, + projectionRevision: input.evaluation.sourceStateProjectionRevision + 1, + }; + try { + assertLegalAutonomyProjection(terminalProjection); + } catch (error) { + return failure("ILLEGAL_STATE", "terminalProjection", error instanceof Error ? error.message : String(error)); + } + const result: WorkflowResult & { outcome: "completed" } = { + outcome: "completed", + reasonCode: null, + retryable: false, + intentUuid: input.current.intentUuid, + autonomyMode: input.current.mode, + grant: grant === null ? null : { id: grant.grantId, state: "completed" }, + evidenceFingerprint: null, + resumeCondition: null, + failureRef: null, + }; + const eventResults = [ + ...(grant === null ? [] : [terminalEvent("INTENT_GRANT_COMPLETED", evidence.intentUuid, { + grantId: grant.grantId, + state: grant.state, + evidenceId: evidence.evidenceId, + })]), + terminalEvent("WORKFLOW_STATE_CLEARED", evidence.intentUuid, { + workflowExecutionState: null, + evidenceId: evidence.evidenceId, + }), + terminalEvent("WORKFLOW_COMPLETED", evidence.intentUuid, { + result, + evidenceId: evidence.evidenceId, + evidenceDigest: evidence.evidenceDigest, + }), + ]; + const terminalEvents: AuditEventPlan[] = []; + for (const event of eventResults) { + if (!event.ok) return event; + terminalEvents.push(event.value); + } + const orderedEvents = [input.evaluation.audit[0], ...terminalEvents]; + const expectedEventIdentities = orderedEvents.map((event) => event.eventIdentity); + const transactionId = stableId("intent-terminal-transaction", "amadeus.intent-terminal-transaction.v1", [ + { tag: "intent", value: evidence.intentUuid }, + { tag: "evidence-id", value: evidence.evidenceId }, + { tag: "evidence-digest", value: evidence.evidenceDigest }, + { tag: "cohort", value: evidence.cohort.cohortDigest }, + { tag: "events", value: expectedEventIdentities.join("\0") }, + { tag: "audit-revision", value: String(input.evaluation.sourceAuditRevision) }, + { tag: "projection-revision", value: String(input.evaluation.sourceStateProjectionRevision) }, + ]); + const completionSeal = digest("intent-completion-seal", { + transactionId, + evidenceId: evidence.evidenceId, + evidenceDigest: evidence.evidenceDigest, + terminalProjection, + result, + }); + if (!completionSeal.ok) return completionSeal; + return success({ + transaction: { transactionId, expectedRevision: input.evaluation.sourceAuditRevision }, + orderedEvents, + completionEvidence: evidence, + expectedEventIdentities, + expectedStateProjectionRevision: input.evaluation.sourceStateProjectionRevision + 1, + terminalProjection, + completionSealDigest: completionSeal.value, + result, + }); +} + +export function acceptTerminalCommit(input: { + readonly plan: TerminalCommitPlan; + readonly receipt: AuditCommitReceipt; +}): ContractResult { + if (input.receipt.transactionId !== input.plan.transaction.transactionId || + input.receipt.stateProjectionRevision !== input.plan.expectedStateProjectionRevision || + input.receipt.committedEventIdentities.length !== input.plan.expectedEventIdentities.length || + input.receipt.committedEventIdentities.some((identity, index) => identity !== input.plan.expectedEventIdentities[index])) { + return failure("CONFLICT", "terminalCommitReceipt", "terminal transaction receipt is partial or mismatched"); + } + return success({ + evidenceId: input.plan.completionEvidence.evidenceId, + transactionId: input.receipt.transactionId, + committedEventIdentities: input.receipt.committedEventIdentities, + stateProjectionRevision: input.receipt.stateProjectionRevision, + completionSealDigest: input.plan.completionSealDigest, + terminalProjection: input.plan.terminalProjection, + result: input.plan.result, + }); +} + +export interface IntentCompletionLedgerSnapshot { + readonly schemaVersion: 1; + readonly auditRevision: number; + readonly stateProjectionRevision: number; + readonly committedTransactions: readonly { + readonly transactionId: StableId; + readonly events: readonly AuditEventPlan[]; + readonly receipt: AuditCommitReceipt; + }[]; + readonly digest: Sha256Digest; +} + +export interface IntentCompletionLedger { + commit(plan: TerminalCommitPlan): ContractResult; + exportSnapshot(): IntentCompletionLedgerSnapshot; +} + +export function createMemoryIntentCompletionLedger( + input: { readonly auditRevision: number; readonly stateProjectionRevision: number; readonly snapshot?: IntentCompletionLedgerSnapshot }, +): IntentCompletionLedger { + let auditRevision = input.auditRevision; + let stateProjectionRevision = input.stateProjectionRevision; + const transactions = new Map(); + if (input.snapshot !== undefined) { + const { digest: snapshotDigest, ...value } = input.snapshot; + const observed = digest("intent-completion-ledger", value); + if (!observed.ok || observed.value !== snapshotDigest) throw new Error("invalid-intent-completion-ledger-snapshot"); + auditRevision = input.snapshot.auditRevision; + stateProjectionRevision = input.snapshot.stateProjectionRevision; + for (const transaction of input.snapshot.committedTransactions) { + transactions.set(transaction.transactionId, { events: transaction.events, receipt: transaction.receipt }); + } + } + return { + commit(plan) { + const replay = transactions.get(plan.transaction.transactionId); + if (replay !== undefined) return acceptTerminalCommit({ plan, receipt: replay.receipt }); + if (auditRevision !== plan.transaction.expectedRevision || + stateProjectionRevision + 1 !== plan.expectedStateProjectionRevision) { + return failure("CONFLICT", "terminalCas", "audit or state projection revision changed"); + } + const receipt: AuditCommitReceipt = { + transactionId: plan.transaction.transactionId, + committedEventIdentities: [...plan.expectedEventIdentities], + auditRevision: auditRevision + plan.orderedEvents.length, + stateProjectionRevision: plan.expectedStateProjectionRevision, + }; + // Publish only after the complete receipt is constructed. A thrown + // validation leaves every externally observable field unchanged. + const accepted = acceptTerminalCommit({ plan, receipt }); + if (!accepted.ok) return accepted; + transactions.set(plan.transaction.transactionId, { events: plan.orderedEvents, receipt }); + auditRevision = receipt.auditRevision; + stateProjectionRevision = receipt.stateProjectionRevision; + return accepted; + }, + exportSnapshot() { + const value = { + schemaVersion: 1 as const, + auditRevision, + stateProjectionRevision, + committedTransactions: [...transactions].sort(([left], [right]) => compareUtf8(left, right)).map( + ([transactionId, transaction]) => ({ transactionId, ...transaction }), + ), + }; + const snapshotDigest = digest("intent-completion-ledger", value); + if (!snapshotDigest.ok) throw new Error(snapshotDigest.error.detail); + return { ...value, digest: snapshotDigest.value }; + }, + }; +} + +export function completedReviewSeed(plan: TerminalCommitPlan, auditRevision: number): ReviewIntentSeed { + return { + intentUuid: plan.completionEvidence.intentUuid, + lifecycle: "completed", + autonomy: plan.terminalProjection, + auditRevision, + completionSealDigest: plan.completionSealDigest, + }; +} + +export interface AwaitingLiveCapability { + readonly outcome: "AWAITING_HUMAN"; + readonly intentUuid: StableId; + readonly missingHarnessIds: readonly CompletionHarnessId[]; + readonly resumeCondition: { + readonly kind: "human-or-capability"; + readonly identity: StableId; + readonly status: "pending"; + readonly evidenceFingerprint: Sha256Digest; + }; +} + +export function awaitingLiveCapability( + intentUuid: StableId, + cohort: RequiredCompletionCohort, + missingHarnessIds: readonly CompletionHarnessId[], +): ContractResult { + const uniqueMissing = [...new Set(missingHarnessIds)].sort(compareUtf8); + if (!SAFE_ID.test(intentUuid) || uniqueMissing.length === 0 || + uniqueMissing.some((harnessId) => !cohort.harnessIds.includes(harnessId))) { + return failure("MALFORMED", "missingHarnesses", "awaiting-human route requires missing cohort members"); + } + const evidenceFingerprint = digest("missing-live-capability", { + intentUuid, + cohortDigest: cohort.cohortDigest, + missingHarnessIds: uniqueMissing, + }); + if (!evidenceFingerprint.ok) return evidenceFingerprint; + return success({ + outcome: "AWAITING_HUMAN", + intentUuid, + missingHarnessIds: uniqueMissing, + resumeCondition: { + kind: "human-or-capability", + identity: stableId("live-capability-resume", "amadeus.live-capability-resume.v1", [ + { tag: "intent", value: intentUuid }, + { tag: "cohort", value: cohort.cohortDigest }, + { tag: "missing", value: uniqueMissing.join("\0") }, + ]), + status: "pending", + evidenceFingerprint: evidenceFingerprint.value, + }, + }); +} + +export function parseCompletionEvidencePayload(event: CompletionEvidenceValidatedEventPlan): ContractResult> { + try { + const parsed = JSON.parse(event.fields.payload_v1 ?? "null") as unknown; + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || !exactlyKeys(parsed as Record, [ + "schemaVersion", "evidenceId", "intentUuid", "cohortId", "cohortDigest", "implementationRevision", + "packageDigest", "registryDigest", "scenarioDigest", "harnessIds", "receiptIds", "authorizationIds", + "validationEventIdentities", "validationDigests", "observationProofDigests", "sourceAuditRevisions", + "evidenceDigest", "sourceAuditRevision", + ])) return failure("MALFORMED", "completionEvidencePayload", "payload is not closed v1"); + const observed = digest("completion-evidence-validated-payload", parsed); + return observed.ok && observed.value === event.payloadDigest + ? success(parsed as Record) + : failure("CONFLICT", "completionEvidencePayload", "payload digest mismatch"); + } catch (error) { + return failure("MALFORMED", "completionEvidencePayload", error instanceof Error ? error.message : String(error)); + } +} diff --git a/packages/framework/core/tools/amadeus-loop-monitor-replay.ts b/packages/framework/core/tools/amadeus-loop-monitor-replay.ts index 4249dcc84..a9143a5e6 100644 --- a/packages/framework/core/tools/amadeus-loop-monitor-replay.ts +++ b/packages/framework/core/tools/amadeus-loop-monitor-replay.ts @@ -154,6 +154,7 @@ const EVENT_TYPES = new Set([ "LOOP_LATCH_CLEARED", "WORKFLOW_UNPARKED", "LOOP_JUDGE_AWAITING_HUMAN", + "LIVE_SMOKE_AUTHORIZED", ]); function validTrace(value: unknown): boolean { @@ -227,6 +228,9 @@ function validEvent(value: unknown): value is LoopMonitorEvent { return typeof value.monitorId === "string" && typeof value.reasonCode === "string"; case "LOOP_JUDGE_AWAITING_HUMAN": return typeof value.invocationId === "string" && typeof value.reason === "string"; + case "LIVE_SMOKE_AUTHORIZED": + return typeof value.authorizationId === "string" && typeof value.actorId === "string" && + typeof value.scopeDigest === "string"; } } diff --git a/packages/framework/core/tools/amadeus-loop-monitor-runtime.ts b/packages/framework/core/tools/amadeus-loop-monitor-runtime.ts index a67876f47..2028983ad 100644 --- a/packages/framework/core/tools/amadeus-loop-monitor-runtime.ts +++ b/packages/framework/core/tools/amadeus-loop-monitor-runtime.ts @@ -99,7 +99,13 @@ export type LoopMonitorEvent = readonly humanTurnId: string | null; } | { readonly type: "WORKFLOW_UNPARKED"; readonly monitorId: string; readonly reasonCode: string } - | { readonly type: "LOOP_JUDGE_AWAITING_HUMAN"; readonly invocationId: string; readonly reason: string }; + | { readonly type: "LOOP_JUDGE_AWAITING_HUMAN"; readonly invocationId: string; readonly reason: string } + | { + readonly type: "LIVE_SMOKE_AUTHORIZED"; + readonly authorizationId: string; + readonly actorId: string; + readonly scopeDigest: string; + }; export interface LoopMonitorEventSet { readonly eventSetId: string; @@ -288,6 +294,7 @@ const RUNTIME_EVENT_HANDLERS = { LOOP_LATCH_SET: (projection, _monitor, event) => ({ ...projection, latch: event.latch, pendingJudge: null }), LOOP_LATCH_CLEARED: (projection) => ({ ...projection, latch: null, awaitingHumanReason: null }), WORKFLOW_UNPARKED: (projection) => projection, + LIVE_SMOKE_AUTHORIZED: (projection) => projection, LOOP_JUDGE_AWAITING_HUMAN: (projection, _monitor, event) => ({ ...projection, awaitingHumanReason: event.reason, @@ -452,6 +459,34 @@ export function verifyHumanRetry(value: { readonly eventType: string; readonly a : null; } +// The credential-attested authorization seam for opt-in live smoke runs +// (#2067 Bolt 5). The port is supplied by the harness environment; the +// coordinator only records the granted authorization as a partition event and +// never invents one. +export interface LiveAuthorizationPort { + authorize(request: { + readonly intentUuid: string; + readonly monitorId: string; + readonly scopeDigest: string; + readonly harnessId?: string; + readonly implementationRevision?: string; + readonly packageDigest?: string; + readonly registryDigest?: string; + readonly scenarioDigest?: string; + }): + | { + readonly authorized: true; + readonly authorizationId: string; + readonly actorId: string; + readonly environmentId?: string; + readonly issuerPrincipalId?: string; + readonly traceId?: string; + readonly spanId?: string; + readonly attestationDigest?: string; + } + | { readonly authorized: false; readonly reason: string }; +} + export interface LoopMonitorCoordinator { observeDelivery(delivery: LoopDelivery): LoopMonitorCoordinatorResult; dispatchJudge(permit: CommittedJudgeDispatchPermit, port: JudgePort): LoopMonitorCoordinatorResult; @@ -460,6 +495,11 @@ export interface LoopMonitorCoordinator { readonly partition: LoopMonitorPartition; readonly humanRetry?: VerifiedHumanTurn; }): LoopMonitorCoordinatorResult; + authorizeLiveSmoke( + partition: LoopMonitorPartition, + scopeDigest: string, + port: LiveAuthorizationPort, + ): { readonly kind: "authorized"; readonly receipt: LoopMonitorCommitReceipt } | { readonly kind: "CONFLICT"; readonly reason: string }; readProjection(partition: LoopMonitorPartition): LoopMonitorRuntimeProjection; } @@ -811,6 +851,25 @@ export function createLoopMonitorCoordinator(options: { }); }, + authorizeLiveSmoke(partition, scopeDigest, port) { + if (resolve(partition) === null) return { kind: "CONFLICT", reason: "partition-not-in-compiled-graph" }; + const authorization = port.authorize({ + intentUuid: partition.intentUuid, + monitorId: partition.monitorId, + scopeDigest, + }); + if (!authorization.authorized) return { kind: "CONFLICT", reason: authorization.reason }; + return repository.transaction(partition, (_sets, append) => { + const receipt = append(eventSet(partition, `live-smoke:${authorization.authorizationId}`, authorization, [{ + type: "LIVE_SMOKE_AUTHORIZED", + authorizationId: authorization.authorizationId, + actorId: authorization.actorId, + scopeDigest, + }])); + return { kind: "authorized", receipt }; + }); + }, + readProjection: read, }; } diff --git a/packages/framework/harness/registry.ts b/packages/framework/harness/registry.ts new file mode 100644 index 000000000..2c5918534 --- /dev/null +++ b/packages/framework/harness/registry.ts @@ -0,0 +1,3 @@ +// Build-time compatibility export. The projected Core registry is the single +// authoring source consumed by runtime and packaging. +export * from "../core/tools/amadeus-harness-registry.ts"; diff --git a/tests/.coverage-ratchet.json b/tests/.coverage-ratchet.json index 2c9da7a6e..fefeb643f 100644 --- a/tests/.coverage-ratchet.json +++ b/tests/.coverage-ratchet.json @@ -2,7 +2,7 @@ "note": "Committed baseline: covered-unit count per class. The --check ratchet fails CI if any class's covered count DROPS below these numbers without a reviewed deferred entry. Monotonic anti-regression: you can cover more, never silently less. Regenerate with: bun tests/gen-coverage-registry.ts", "coveredByClass": { "function": 175, - "audit": 43, + "audit": 44, "scope": 15, "stage": 8, "hook": 14, diff --git a/tests/.coverage-registry.json b/tests/.coverage-registry.json index f648c3df9..1343490c4 100644 --- a/tests/.coverage-registry.json +++ b/tests/.coverage-registry.json @@ -20,10 +20,10 @@ "render-surface": "tui" }, "counts": { - "total": 604, + "total": 605, "enumeratedByClass": { "function": 339, - "audit": 89, + "audit": 90, "scope": 15, "stage": 32, "hook": 14, @@ -32,7 +32,7 @@ }, "coveredByClass": { "function": 175, - "audit": 43, + "audit": 44, "scope": 15, "stage": 8, "hook": 14, @@ -340,6 +340,18 @@ ], "status": "covered" }, + { + "unitClass": "audit", + "unitId": "INTENT_COMPLETION_TRANSACTION_COMMITTED", + "minMechanism": "none", + "coveredBy": [ + { + "file": "tests/unit/t434-intent-completion.test.ts", + "mechanism": "none" + } + ], + "status": "covered" + }, { "unitClass": "audit", "unitId": "INTENT_UNARCHIVED", diff --git a/tests/integration/event-registry-drift.test.ts b/tests/integration/event-registry-drift.test.ts index cb1eae375..3bebc5ed9 100644 --- a/tests/integration/event-registry-drift.test.ts +++ b/tests/integration/event-registry-drift.test.ts @@ -47,11 +47,11 @@ describe("registry internal consistency (VER-1)", () => { expect(() => assertRegistryConsistent()).not.toThrow(); }); - test("canonical cardinality is pinned at 89 (#1672, #1602, #1919, #2095, #2096, #2067) — vacuous equality banned", () => { - expect(EXPECTED_CANONICAL_COUNT).toBe(89); - expect(canonicalAuditEvents().length).toBe(89); - expect(SETS.registryCanonical.size).toBe(89); - expect(SETS.auditVocabulary.size).toBe(89); + test("canonical cardinality is pinned at 90 (#1672, #1602, #1919, #2095, #2096, #2067) — vacuous equality banned", () => { + expect(EXPECTED_CANONICAL_COUNT).toBe(90); + expect(canonicalAuditEvents().length).toBe(90); + expect(SETS.registryCanonical.size).toBe(90); + expect(SETS.auditVocabulary.size).toBe(90); }); test("canonical defs always map to the audit journal; telemetry defs never do (FR-EXP-4)", () => { @@ -189,7 +189,7 @@ describe("extraction parity with the t28 vocabulary guard", () => { test("extractAuditVocabulary agrees with the t28 sed-range rule on the shipped amadeus-audit.ts", () => { const body = readFileSync(join(AMADEUS_SRC, "tools", "amadeus-audit.ts"), "utf-8"); const vocab = extractAuditVocabulary(body); - expect(vocab.length).toBe(89); + expect(vocab.length).toBe(90); expect(vocab).toEqual([...SETS.auditVocabulary].sort()); }); }); diff --git a/tests/integration/t432-intent-autonomy-runtime.integration.test.ts b/tests/integration/t432-intent-autonomy-runtime.integration.test.ts index 9d3029aba..4fdd7a1b7 100644 --- a/tests/integration/t432-intent-autonomy-runtime.integration.test.ts +++ b/tests/integration/t432-intent-autonomy-runtime.integration.test.ts @@ -486,7 +486,7 @@ describe("Intent autonomy durable coordinator", () => { expect(projectIntentAutonomyStatus(coordinator.readProjection())).toMatchObject({ autonomyMode: "full", workflowExecutionState: "running", - terminalLiveCompletionCapable: false, + terminalLiveCompletionCapable: true, legacyStandingGrantCount: 0, }); }); diff --git a/tests/integration/t433-autonomy-review-observability.test.ts b/tests/integration/t433-autonomy-review-observability.test.ts index a80cde034..bd0a67b22 100644 --- a/tests/integration/t433-autonomy-review-observability.test.ts +++ b/tests/integration/t433-autonomy-review-observability.test.ts @@ -1034,30 +1034,16 @@ describe("production review projection", () => { }); const completionSealDigest = `sha256:${"c".repeat(64)}`; - // Raw shard append: INTENT_COMPLETION_TRANSACTION_COMMITTED is Bolt 5 - // vocabulary the registry does not carry yet; the review production path - // only READS the seal off the shards, so the fixture seeds the row - // directly instead of routing it through the registry-guarded emitter. - appendFileSync( - join(projectDir, "amadeus", "spaces", "default", "intents", DEFAULT_RECORD_DIR, "audit", "review-completion-seed.jsonl"), - `${JSON.stringify({ - schemaVersion: 1, - seq: 1, - cloneId: "review-completion-seed", - intentId: DEFAULT_RECORD_DIR, - timestamp: new Date().toISOString(), - heading: "Intent Completion Transaction Committed", - event: "INTENT_COMPLETION_TRANSACTION_COMMITTED", - fields: { - "Intent Uuid": DEFAULT_INTENT_UUID, - "Transaction Id": "review-completion-transaction", - "Evidence Id": "review-completion-evidence", - "Evidence Digest": completionSealDigest, - "Completion Seal Digest": completionSealDigest, - Transaction: "{}", - }, - })}\n`, - ); + // Bolt 5 registered INTENT_COMPLETION_TRANSACTION_COMMITTED as canonical + // vocabulary, so the fixture seeds the seal through the guarded emitter. + emitAuditEventGuarded("INTENT_COMPLETION_TRANSACTION_COMMITTED", { + "Intent Uuid": DEFAULT_INTENT_UUID, + "Transaction Id": "review-completion-transaction", + "Evidence Id": "review-completion-evidence", + "Evidence Digest": completionSealDigest, + "Completion Seal Digest": completionSealDigest, + Transaction: "{}", + }, projectDir, DEFAULT_RECORD_DIR, "default"); expect(withLockedIntentRegistry( projectDir, (context) => transitionIntentStatusLocked(context, DEFAULT_RECORD_DIR, "complete"), diff --git a/tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts b/tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts new file mode 100644 index 000000000..f469e4efb --- /dev/null +++ b/tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts @@ -0,0 +1,29 @@ +// covers: file:packages/framework/core/tools/amadeus-harness-registry.ts +// size: medium + +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +const ROOT = join(import.meta.dir, "..", ".."); +const CORE = join(ROOT, "packages", "framework", "core", "tools"); +const HARNESSES = [ + ["claude", ".claude"], + ["codex", ".codex"], + ["cursor", ".cursor"], + ["opencode", ".opencode"], + ["kimi", ".kimi-code"], +] as const; +const FILES = ["amadeus-harness-registry.ts", "amadeus-intent-completion.ts"] as const; + +describe("Intent completion current five-harness projection", () => { + for (const [harness, directory] of HARNESSES) { + test(`${harness} consumes byte-identical shared completion Core`, () => { + for (const file of FILES) { + expect(readFileSync(join(ROOT, "dist", harness, directory, "tools", file))).toEqual( + readFileSync(join(CORE, file)), + ); + } + }); + } +}); diff --git a/tests/integration/t434-intent-completion-live-seam.integration.test.ts b/tests/integration/t434-intent-completion-live-seam.integration.test.ts new file mode 100644 index 000000000..6910b41dc --- /dev/null +++ b/tests/integration/t434-intent-completion-live-seam.integration.test.ts @@ -0,0 +1,61 @@ +// covers: file:packages/framework/core/tools/amadeus-intent-completion.ts, file:packages/framework/core/tools/amadeus-harness-registry.ts +// size: medium + +import { describe, expect, test } from "bun:test"; +import { HARNESS_REGISTRY } from "../../packages/framework/core/tools/amadeus-harness-registry.ts"; + +const LIVE_HARNESSES = HARNESS_REGISTRY.filter((descriptor) => descriptor.autonomyLive); + +const LIVE_ENABLED = process.env.AMADEUS_INTENT_COMPLETION_LIVE === "1"; + +function commandFor(environmentName: string): string[] { + const raw = process.env[environmentName]; + if (raw === undefined) throw new Error(`missing live command: ${environmentName}`); + const parsed = JSON.parse(raw) as unknown; + if (!Array.isArray(parsed) || parsed.length === 0 || parsed.some((part) => typeof part !== "string" || part.length === 0)) { + throw new Error(`${environmentName} must be a non-empty JSON string array`); + } + return parsed; +} + +describe("opt-in five-harness credential seam", () => { + test.skipIf(!LIVE_ENABLED)("executes every native harness command and validates Judge/election observations", async () => { + expect(LIVE_HARNESSES.map((descriptor) => descriptor.id)).toEqual([ + "claude", "codex", "cursor", "opencode", "kimi", + ]); + const receipts = await Promise.all(LIVE_HARNESSES.map(async (descriptor) => { + const environmentName = descriptor.native.liveCommandEnv; + if (environmentName === null) throw new Error(`live harness ${descriptor.id} has no command adapter`); + const [executable, ...args] = commandFor(environmentName); + const child = Bun.spawn([executable, ...args], { + stdin: new Blob([JSON.stringify({ + schema_version: 1, + harness_id: descriptor.id, + scenario: "intent-autonomy-judge-election", + required_observations: ["judge-invoked", "solo-election-or-loud-degradation"], + })]), + stdout: "pipe", + stderr: "pipe", + timeout: 25_000, + }); + const [exitCode, stdout, stderr] = await Promise.all([ + child.exited, + new Response(child.stdout).text(), + new Response(child.stderr).text(), + ]); + if (exitCode !== 0) throw new Error(`${descriptor.id} live command failed (${exitCode}): ${stderr}`); + const output = stdout.trim(); + if (output.length === 0) throw new Error(`${descriptor.id} live command produced no receipt`); + const receipt = JSON.parse(output.split("\n").at(-1)!) as Record; + return { descriptor, receipt }; + })); + for (const { descriptor, receipt } of receipts) { + expect(receipt.harness_id).toBe(descriptor.id); + expect(receipt.success).toBe(true); + expect(receipt.judge_invoked).toBe(true); + expect(["solo-election", "loud-degradation"]).toContain(String(receipt.decision_path)); + expect(typeof receipt.implementation_revision).toBe("string"); + expect(typeof receipt.package_digest).toBe("string"); + } + }); +}); diff --git a/tests/integration/t48-audit-event-emitters.test.ts b/tests/integration/t48-audit-event-emitters.test.ts index dd3227f32..8ab549f69 100644 --- a/tests/integration/t48-audit-event-emitters.test.ts +++ b/tests/integration/t48-audit-event-emitters.test.ts @@ -132,17 +132,19 @@ const ALL_SOURCE_FILES = [...tsFiles(TOOLS_DIR), ...tsFiles(HOOKS_DIR)]; /** * has_emission (.sh:40-60): is there a LIVE emission call site for `event` in - * `text`? Three patterns, on the decommented view: + * `text`? Four patterns, on the decommented view: * 1. helper(...,"EVENT"...) on one line * 2. eventType = ... "EVENT" (audit-logger ternary) * 3. an indented `"EVENT" ,` line (multi-line helper, literal on own line) + * 4. `eventType: "EVENT"` (typed repository event construction) */ function hasEmission(event: string, text: string): boolean { const live = decommented(text); const p1 = new RegExp(`${EMITTERS}\\([^)]*"${event}"`); const p2 = new RegExp(`eventType = [^;]*"${event}"`); const p3 = new RegExp(`^[ \\t]+"${event}"[ \\t]*(/\\*[^*]*\\*/)?[ \\t]*,`, "m"); - return p1.test(live) || p2.test(live) || p3.test(live); + const p4 = new RegExp(`eventType:\\s*"${event}"`); + return p1.test(live) || p2.test(live) || p3.test(live) || p4.test(live); } /** Read the emitter taxonomy registry rows from 12-state-machine.md. diff --git a/tests/no-silent-drop/baseline.json b/tests/no-silent-drop/baseline.json index d77175920..5f8f15c45 100644 --- a/tests/no-silent-drop/baseline.json +++ b/tests/no-silent-drop/baseline.json @@ -2,10 +2,10 @@ "schemaVersion": 1, "direction": "shrink-only", "generatedFrom": { - "revision": "7034aac35acfac1d00b98bc8d6fd8e3c1b2804c6", + "revision": "2e990c45a4cf034c9b4c6a68b1cafed0bea48fcd", "censusDigest": "b6bf74ffc7810e2a619424bf3992eeeab321b511f9fe8cce9876808b5ea34c2c", "approvalDigest": "0e92854570e9dacf4664fd19ff7074b8538b32fd59283d1d48252925015ca6df", - "previousDigest": "1041a18e3d1ceb0f624d2131f585945bf27933585421c8291e3688bde5e60c08" + "previousDigest": "67b9d0fb733ded0aee77fc6bf2dd6353772836a647e695882e8932f83e172331" }, "entries": [ { diff --git a/tests/no-silent-drop/exemptions.json b/tests/no-silent-drop/exemptions.json index 02fed07c8..38543b0e0 100644 --- a/tests/no-silent-drop/exemptions.json +++ b/tests/no-silent-drop/exemptions.json @@ -1,5 +1,5 @@ { "schemaVersion": 1, - "previousDigest": "e686c8f6b5ffac9a3abe37141b1310d501ac2f5dd3a3a05c6324eb691bd6e280", + "previousDigest": "c28807a8a13ce6259079f82d2273e52687197c9d63eff6040d8d1e65dd77897b", "entries": [] } diff --git a/tests/unit/t28-audit-event-sync.test.ts b/tests/unit/t28-audit-event-sync.test.ts index 9b18734b8..1788c7ed5 100644 --- a/tests/unit/t28-audit-event-sync.test.ts +++ b/tests/unit/t28-audit-event-sync.test.ts @@ -77,8 +77,9 @@ const AUDIT_MD = join(AMADEUS_SRC, "knowledge", "amadeus-shared", "audit-format. // +LOOP_MONITOR_EVENT_SET_COMMITTED (#2095) takes it to 86; // +QUALITY_REPAIR_TRANSACTION_COMMITTED (#2096) takes it to 87; // +INTENT_AUTONOMY_TRANSACTION_COMMITTED (#2067) takes it to 88. -// +AUTO_DECISION_REVIEWED (#2067 review surface) takes it to 89. -const CANONICAL_COUNT = 89; +// +AUTO_DECISION_REVIEWED (#2067 review surface) takes it to 89; +// +INTENT_COMPLETION_TRANSACTION_COMMITTED (#2067 completion seal) takes it to 90. +const CANONICAL_COUNT = 90; /** Slice the lines of `text` BETWEEN the first line matching `start` and the * next line matching `end` (inclusive of both), reproducing `sed -n @@ -181,7 +182,7 @@ describe("t28 audit event-type sync (migrated from t28-audit-event-sync.sh, plan // .sh test 7: assert_eq TS_COUNT - the canonical baseline pin, bumped when // events are added or removed. (#367 added WORKFLOW_PARKED/UNPARKED -> 69; // #369 removed TEST_RUN_MODE_ENABLED -> 68; HUMAN_TURN took it to 69; the adaptive composer added RECOMPOSED -> 70; #671 added DELEGATED_APPROVAL -> 71; #685 added DELEGATED_REJECTION -> 72; #499/#848 added GUARD_EXEMPTED -> 73; #1125 added GRANT_ISSUED + GRANT_REVOKED -> 75; lifecycle transactions add two -> 77.) - test("VALID_EVENT_TYPES.size === 89 (baseline pin) [.sh test 7]", () => { + test("VALID_EVENT_TYPES.size === 90 (baseline pin) [.sh test 7]", () => { expect(TS_EVENTS.length).toBe(CANONICAL_COUNT); }); }); diff --git a/tests/unit/t434-intent-completion.test.ts b/tests/unit/t434-intent-completion.test.ts new file mode 100644 index 000000000..76349e077 --- /dev/null +++ b/tests/unit/t434-intent-completion.test.ts @@ -0,0 +1,502 @@ +// covers: file:packages/framework/core/tools/amadeus-intent-completion.ts file:packages/framework/harness/registry.ts audit:INTENT_COMPLETION_TRANSACTION_COMMITTED +// size: medium + +import { describe, expect, test } from "bun:test"; + +import { + acceptTerminalCommit, + awaitingLiveCapability, + completedReviewSeed, + createIntentCompletionEvaluator, + createIntentLiveAuthorizationService, + createIntentLiveReceiptValidator, + createIntentLiveRunCoordinator, + createMemoryIntentCompletionLedger, + parseCompletionEvidencePayload, + planTerminalCommit, + resolveRequiredCompletionCohort, + validateHarnessRegistry, + type AuditCommitReceipt, + type CanonicalAuditEvent, + type CommittedIntentLiveExecutionAuthorization, + type CommittedValidatedIntentLiveReceipt, + type CanonicalLiveRunSnapshot, + type LiveScenarioRevision, + type RawIntentLiveReceipt, + type IntentCompletionEvaluation, +} from "../../packages/framework/core/tools/amadeus-intent-completion.ts"; +import { + autonomyDigest, + createAutonomyProjection, + grantIssuanceDisplayDigest, + normalizeDecisionPolicies, + planHumanAutonomyCommand, + type AutonomyProjection, + type GrantScopeDescriptor, + type HumanCommandContext, +} from "../../packages/framework/core/tools/amadeus-intent-autonomy.ts"; +import { + canonicalContractValueDigest, + createMemoryAutonomyReviewService, +} from "../../packages/framework/core/tools/amadeus-autonomy-review.ts"; + +const INTENT = "019fc5ac-f0bb-7a5f-8a64-c944b6f76ead"; +const IMPLEMENTATION = "revision-a1b2c3"; +const PACKAGE = autonomyDigest("package"); +const SCENARIO = autonomyDigest("scenario"); +const HUMAN = { verified: true, eventType: "HUMAN_TURN", actor: "human", turnId: "human-turn-1" } as const; + +function isCompleteEvaluation( + value: IntentCompletionEvaluation, +): value is Extract { + return value.check.kind === "complete"; +} + +function commitReceipt( + transactionId: string, + eventIdentity: string, + auditRevision: number, + stateProjectionRevision = auditRevision, +): AuditCommitReceipt { + return { transactionId, committedEventIdentities: [eventIdentity], auditRevision, stateProjectionRevision }; +} + +function fullProjection(): AutonomyProjection { + const initial = createAutonomyProjection({ intentUuid: INTENT }); + const scope: GrantScopeDescriptor = { + intentUuid: INTENT, + scopeId: "self-feature", + scopeFingerprint: autonomyDigest("self-feature"), + normFingerprint: autonomyDigest("norm"), + allowedInteractionKinds: ["stage-gate", "phase-gate", "walking-skeleton", "question"], + permissionBoundaryFingerprint: autonomyDigest("permission-boundary"), + prohibitedEffects: ["new-permission", "irreversible", "scope-out", "norm-waiver", "quality-waiver"], + }; + const policies = normalizeDecisionPolicies({ + grantIdentitySeed: "grant-seed", + scopeFingerprint: scope.scopeFingerprint, + humanTurnId: HUMAN.turnId, + policies: [{ sourceText: "Choose the approved safe option", selector: "selector-1", optionId: "accept" }], + }); + const display = grantIssuanceDisplayDigest({ intentUuid: INTENT, principalId: "principal-1", scope, policies }); + const context: HumanCommandContext = { + targetIntentUuid: INTENT, + principalId: "principal-1", + humanTurn: HUMAN, + commandOccurrenceId: "command-1", + expectedProjectionRevision: initial.projectionRevision, + confirmedDisplayDigest: display, + }; + const plan = planHumanAutonomyCommand(initial, { kind: "issue-full", scope, policies }, context); + if (!plan.ok) throw new Error(plan.code); + return plan.after; +} + +async function buildFiveHarnessEvidence() { + const registry = validateHarnessRegistry(); + if (!registry.ok) throw new Error(registry.error.detail); + const cohort = resolveRequiredCompletionCohort(registry.value); + if (!cohort.ok) throw new Error(cohort.error.detail); + const revision: LiveScenarioRevision = { + implementationRevision: IMPLEMENTATION, + packageDigest: PACKAGE, + registryDigest: registry.value.registryDigest, + scenarioDigest: SCENARIO, + }; + const authorizations: CommittedIntentLiveExecutionAuthorization[] = []; + const rawReceipts: RawIntentLiveReceipt[] = []; + const validations: CommittedValidatedIntentLiveReceipt[] = []; + for (const [index, harnessId] of cohort.value.harnessIds.entries()) { + const authorizationService = createIntentLiveAuthorizationService({ + port: { + authorize: () => ({ + authorized: true, + authorizationId: `provider-auth-${harnessId}`, + actorId: `actor-${harnessId}`, + environmentId: `environment-${harnessId}`, + issuerPrincipalId: `issuer-${harnessId}`, + traceId: `trace-${harnessId}`, + spanId: `span-${harnessId}`, + attestationDigest: autonomyDigest(`attestation-${harnessId}`), + }), + }, + }); + const plannedAuthorization = await authorizationService.authorize({ + intentUuid: INTENT, + harnessId, + revision, + cohort: cohort.value, + registry: registry.value, + }); + if (!plannedAuthorization.ok) throw new Error(plannedAuthorization.error.detail); + const authorizationCommit = commitReceipt( + `authorization-transaction-${harnessId}`, + plannedAuthorization.value.audit[0].eventIdentity, + index * 2 + 1, + ); + const authorization = authorizationService.bindCommit({ + ...plannedAuthorization.value, + receipt: authorizationCommit, + }); + if (!authorization.ok) throw new Error(authorization.error.detail); + authorizations.push(authorization.value); + + const observation = { + judgeInvocationId: `judge-${harnessId}`, + judgeObserved: true as const, + electionDecisionId: `decision-${harnessId}`, + electionOutcome: "elected" as const, + degradationReason: null, + }; + const audit = [ + ["LOOP_JUDGE_STARTED", observation.judgeInvocationId], + ["LOOP_JUDGE_RESULT_OBSERVED", observation.judgeInvocationId], + ["AUTO_DECIDED", observation.electionDecisionId], + ].map(([eventType, eventIdentity], offset): CanonicalAuditEvent => ({ + eventType, + eventIdentity, + intentUuid: INTENT, + fields: { trace_id: authorization.value.traceId }, + payloadDigest: autonomyDigest([eventType, eventIdentity]), + transactionId: `observation-transaction-${harnessId}-${offset}`, + auditRevision: index * 2 + 2, + })); + const rawReceipt: RawIntentLiveReceipt = { + schemaVersion: "1", + receiptId: `receipt-${harnessId}`, + intentUuid: INTENT, + harnessId, + authorizationId: authorization.value.authorizationId, + authorizationEventIdentity: authorization.value.authorizationEventIdentity, + authorizationCommitTransactionId: authorization.value.commitReceipt.transactionId, + revision, + environmentId: authorization.value.environmentId, + traceId: authorization.value.traceId, + spanId: authorization.value.spanId, + attestationDigest: authorization.value.attestationDigest, + outcome: "passed", + observation, + }; + rawReceipts.push(rawReceipt); + const validator = createIntentLiveReceiptValidator({ + evidenceReader: { + readAuthorizationSnapshot: () => ({ ok: true, value: { + intentUuid: INTENT, + audit, + auditRevision: index * 2 + 2, + stateProjectionRevision: index, + authorization: authorization.value, + } }), + }, + }); + const plannedValidation = validator.validate(rawReceipt); + if (!plannedValidation.ok) throw new Error(plannedValidation.error.detail); + const validation = validator.bindCommit({ + planned: plannedValidation.value, + receipt: commitReceipt( + `validation-transaction-${harnessId}`, + plannedValidation.value.audit[0].eventIdentity, + index * 2 + 2, + ), + }); + if (!validation.ok) throw new Error(validation.error.detail); + validations.push(validation.value); + } + const evaluator = createIntentCompletionEvaluator({ + receiptReader: { + readValidationSet: () => ({ ok: true, value: { + intentUuid: INTENT, + auditRevision: 10, + stateProjectionRevision: 7, + receipts: validations, + } }), + }, + }); + return { registry: registry.value, cohort: cohort.value, revision, authorizations, rawReceipts, validations, evaluator }; +} + +describe("five-harness completion cohort", () => { + test("registry resolves the exact current five while retaining Kiro rows", () => { + const registry = validateHarnessRegistry(); + expect(registry.ok).toBe(true); + if (!registry.ok) throw new Error(registry.error.detail); + expect(registry.value.descriptors.map((descriptor) => descriptor.id)).toContain("kiro"); + expect(registry.value.descriptors.map((descriptor) => descriptor.id)).toContain("kiro-ide"); + const cohort = resolveRequiredCompletionCohort(registry.value); + expect(cohort.ok).toBe(true); + if (!cohort.ok) throw new Error(cohort.error.detail); + expect(cohort.value.harnessIds).toEqual(["claude", "codex", "cursor", "kimi", "opencode"]); + }); + + test("credential absence is not pass and produces the designed non-terminal route", async () => { + const registry = validateHarnessRegistry(); + if (!registry.ok) throw new Error(registry.error.detail); + const cohort = resolveRequiredCompletionCohort(registry.value); + if (!cohort.ok) throw new Error(cohort.error.detail); + const service = createIntentLiveAuthorizationService({ + port: { authorize: () => ({ authorized: false, reason: "credential-unavailable" }) }, + }); + const result = await service.authorize({ + intentUuid: INTENT, + harnessId: "claude", + cohort: cohort.value, + registry: registry.value, + revision: { + implementationRevision: IMPLEMENTATION, + packageDigest: PACKAGE, + registryDigest: registry.value.registryDigest, + scenarioDigest: SCENARIO, + }, + }); + expect(result).toMatchObject({ ok: false, error: { code: "PROVENANCE_REQUIRED", locus: "credential" } }); + const awaiting = awaitingLiveCapability(INTENT, cohort.value, ["claude"]); + expect(awaiting).toMatchObject({ ok: true, value: { outcome: "AWAITING_HUMAN", missingHarnessIds: ["claude"] } }); + }); +}); + +describe("receipt validation and terminal transaction", () => { + test("dispatch claim is canonical, single-use, and native retries attach to one operation", async () => { + const fixture = await buildFiveHarnessEvidence(); + const authorization = fixture.authorizations[0]; + let snapshot: CanonicalLiveRunSnapshot | null = null; + let nativeStarts = 0; + let dispatchCalls = 0; + const coordinator = createIntentLiveRunCoordinator({ + runReader: { + readRunSnapshot: ({ intentUuid, runId }) => snapshot !== null && + snapshot.intentUuid === intentUuid && snapshot.run.runId === runId + ? { ok: true, value: snapshot } + : { ok: false, error: { code: "CONFLICT", locus: "run", detail: "run head not found" } }, + }, + proofVerifier: { + verify: ({ reconciliation }) => ({ ok: true, value: { proofDigest: reconciliation.proofDigest } }), + }, + nativePort: { + reconcile: async () => ({ ok: true, value: { + kind: "attested-no-effect", + proofDigest: autonomyDigest("no-effect-proof"), + } }), + dispatch: async (claimed) => { + dispatchCalls += 1; + if (dispatchCalls === 1) nativeStarts += 1; + const key = canonicalContractValueDigest("intent-live-native-dispatch", { + operationReference: claimed.operationReference, + attempt: claimed.attempt, + }); + if (!key.ok) return key; + return { ok: true, value: { + schemaVersion: "1", + dispatchKeyDigest: key.value, + nativeOperationId: "native-operation-1", + outcome: dispatchCalls === 1 ? "started" : "attached", + proofDigest: autonomyDigest("native-operation-proof"), + } }; + }, + }, + }); + const reserved = coordinator.reserve({ authorization }); + expect(reserved.ok).toBe(true); + if (!reserved.ok) throw new Error(reserved.error.detail); + const committedReservation = coordinator.bindStateCommit({ + planned: reserved.value.reservation, + audit: reserved.value.audit, + receipt: commitReceipt("run-reservation-transaction", reserved.value.audit[0].eventIdentity, 11, 8), + }); + if (!committedReservation.ok) throw new Error(committedReservation.error.detail); + snapshot = { + intentUuid: INTENT, + auditRevision: 11, + stateProjectionRevision: 8, + authorization, + run: committedReservation.value, + }; + const started = await coordinator.planNext({ intentUuid: INTENT, runId: committedReservation.value.runId }); + if (!started.ok) throw new Error(started.error.detail); + const committedStarted = coordinator.bindTransitionCommit({ + prior: started.value.prior, + next: started.value.next, + audit: started.value.audit, + receipt: commitReceipt("run-started-transaction", started.value.audit[0].eventIdentity, 12, 9), + }); + expect(committedStarted.ok).toBe(true); + if (!committedStarted.ok) throw new Error(committedStarted.error.detail); + if (committedStarted.value.dispatchPermit === null) throw new Error("run start did not produce a dispatch permit"); + snapshot = { ...snapshot, auditRevision: 12, stateProjectionRevision: 9, run: committedStarted.value.state }; + const claim = coordinator.claimDispatch(committedStarted.value.dispatchPermit); + expect(claim.ok).toBe(true); + if (!claim.ok) throw new Error(claim.error.detail); + const committedClaim = coordinator.bindDispatchClaimCommit({ + permit: committedStarted.value.dispatchPermit, + prior: claim.value.prior, + claimed: claim.value.claimed, + audit: claim.value.audit, + receipt: commitReceipt("run-claim-transaction", claim.value.audit[0].eventIdentity, 13, 10), + }); + if (!committedClaim.ok) throw new Error(committedClaim.error.detail); + const claimedRun = { + ...claim.value.claimed, + stateEventIdentity: committedClaim.value.claimEventIdentity, + stateCommitReceipt: commitReceipt("run-claim-transaction", committedClaim.value.claimEventIdentity, 13, 10), + sourceAuditRevision: 13, + sourceStateProjectionRevision: 10, + }; + snapshot = { ...snapshot, auditRevision: 13, stateProjectionRevision: 10, run: claimedRun }; + expect(coordinator.claimDispatch(committedStarted.value.dispatchPermit)).toMatchObject({ + ok: false, + error: { code: "CONFLICT", locus: "dispatchClaim" }, + }); + expect((await coordinator.dispatch(committedClaim.value))).toMatchObject({ ok: true, value: { outcome: "started" } }); + expect((await coordinator.dispatch(committedClaim.value))).toMatchObject({ ok: true, value: { outcome: "attached" } }); + expect(nativeStarts).toBe(1); + }); + + test("all five exact receipts atomically complete full grant and clear workflow", async () => { + const fixture = await buildFiveHarnessEvidence(); + const evaluation = fixture.evaluator.evaluate({ + intentUuid: INTENT, + cohort: fixture.cohort, + revision: fixture.revision, + validationEventIdentities: fixture.validations.map((receipt) => receipt.validationEventIdentity).reverse(), + }); + expect(evaluation.ok).toBe(true); + if (!evaluation.ok) throw new Error(evaluation.error.detail); + if (!isCompleteEvaluation(evaluation.value)) throw new Error("expected complete five-harness evaluation"); + expect(evaluation.value.check.evidence.receiptIds).toEqual(fixture.cohort.harnessIds.map((id) => `receipt-${id}`)); + expect(parseCompletionEvidencePayload(evaluation.value.audit[0]).ok).toBe(true); + + const plan = planTerminalCommit({ current: fullProjection(), evaluation: evaluation.value }); + expect(plan.ok).toBe(true); + if (!plan.ok) throw new Error(plan.error.detail); + expect(plan.value.orderedEvents.map((event) => event.eventType)).toEqual([ + "LIVE_COMPLETION_EVIDENCE_VALIDATED", + "INTENT_GRANT_COMPLETED", + "WORKFLOW_STATE_CLEARED", + "WORKFLOW_COMPLETED", + ]); + expect(plan.value.terminalProjection.workflowExecutionState).toBeNull(); + expect(plan.value.terminalProjection.currentGrant).toBeNull(); + expect(plan.value.terminalProjection.terminalGrantHistory.at(-1)?.state).toBe("completed"); + expect(plan.value.result.grant?.state).toBe("completed"); + + const ledger = createMemoryIntentCompletionLedger({ auditRevision: 10, stateProjectionRevision: 7 }); + const committed = ledger.commit(plan.value); + expect(committed).toMatchObject({ ok: true, value: { result: { outcome: "completed" }, stateProjectionRevision: 8 } }); + const replay = ledger.commit(plan.value); + expect(replay).toEqual(committed); + + const restored = createMemoryIntentCompletionLedger({ auditRevision: 0, stateProjectionRevision: 0, snapshot: ledger.exportSnapshot() }); + expect(restored.commit(plan.value)).toEqual(committed); + expect(() => createMemoryAutonomyReviewService({ + intents: [completedReviewSeed(plan.value, 14)], + })).not.toThrow(); + }); + + test("missing, duplicate, skip, forged, and revision mismatch never create completion evidence", async () => { + const fixture = await buildFiveHarnessEvidence(); + const missingReader = createIntentCompletionEvaluator({ + receiptReader: { readValidationSet: () => ({ ok: true, value: { + intentUuid: INTENT, + auditRevision: 10, + stateProjectionRevision: 7, + receipts: fixture.validations.slice(0, 4), + } }) }, + }); + const missing = missingReader.evaluate({ + intentUuid: INTENT, + cohort: fixture.cohort, + revision: fixture.revision, + validationEventIdentities: fixture.validations.slice(0, 4).map((receipt) => receipt.validationEventIdentity), + }); + expect(missing).toMatchObject({ ok: true, value: { check: { kind: "incomplete" }, audit: [] } }); + + const duplicateEvaluator = createIntentCompletionEvaluator({ + receiptReader: { readValidationSet: () => ({ ok: true, value: { + intentUuid: INTENT, + auditRevision: 10, + stateProjectionRevision: 7, + receipts: [...fixture.validations, fixture.validations[0]], + } }) }, + }); + const duplicate = duplicateEvaluator.evaluate({ + intentUuid: INTENT, + cohort: fixture.cohort, + revision: fixture.revision, + validationEventIdentities: fixture.validations.map((receipt) => receipt.validationEventIdentity), + }); + expect(duplicate).toMatchObject({ + ok: true, + value: { + check: { + kind: "incomplete", + missingHarnessIds: [], + rejectedReceiptIds: ["receipt-claude"], + }, + audit: [], + }, + }); + + const authorization = fixture.authorizations[0]; + const rawReceipt = fixture.rawReceipts[0]!; + const skipped: RawIntentLiveReceipt = { + schemaVersion: rawReceipt.schemaVersion, + receiptId: rawReceipt.receiptId, + intentUuid: rawReceipt.intentUuid, + harnessId: rawReceipt.harnessId, + authorizationId: rawReceipt.authorizationId, + authorizationEventIdentity: rawReceipt.authorizationEventIdentity, + authorizationCommitTransactionId: rawReceipt.authorizationCommitTransactionId, + revision: rawReceipt.revision, + environmentId: rawReceipt.environmentId, + traceId: rawReceipt.traceId, + spanId: rawReceipt.spanId, + attestationDigest: rawReceipt.attestationDigest, + outcome: "skipped", + observation: null, + }; + const validator = createIntentLiveReceiptValidator({ + evidenceReader: { readAuthorizationSnapshot: () => ({ ok: true, value: { + intentUuid: INTENT, + audit: [], + auditRevision: 10, + stateProjectionRevision: 7, + authorization, + } }) }, + }); + expect(validator.validate(skipped)).toMatchObject({ ok: false, error: { code: "PROVENANCE_REQUIRED" } }); + expect(validator.validate({ ...skipped, outcome: "passed", observation: fixture.validations[0].observation, + attestationDigest: autonomyDigest("forged") })).toMatchObject({ ok: false, error: { code: "CONFLICT" } }); + + const mismatchedRevision = { ...fixture.revision, scenarioDigest: autonomyDigest("other-scenario") }; + const mismatch = fixture.evaluator.evaluate({ + intentUuid: INTENT, + cohort: fixture.cohort, + revision: mismatchedRevision, + validationEventIdentities: fixture.validations.map((receipt) => receipt.validationEventIdentity), + }); + expect(mismatch).toMatchObject({ ok: true, value: { check: { kind: "incomplete" }, audit: [] } }); + }); + + test("partial or forged terminal commit receipt is rejected", async () => { + const fixture = await buildFiveHarnessEvidence(); + const evaluation = fixture.evaluator.evaluate({ + intentUuid: INTENT, + cohort: fixture.cohort, + revision: fixture.revision, + validationEventIdentities: fixture.validations.map((receipt) => receipt.validationEventIdentity), + }); + if (!evaluation.ok) throw new Error(evaluation.error.detail); + if (!isCompleteEvaluation(evaluation.value)) throw new Error("expected complete five-harness evaluation"); + const plan = planTerminalCommit({ current: createAutonomyProjection({ intentUuid: INTENT }), evaluation: evaluation.value }); + if (!plan.ok) throw new Error(plan.error.detail); + const rejected = acceptTerminalCommit({ + plan: plan.value, + receipt: { + transactionId: plan.value.transaction.transactionId, + committedEventIdentities: plan.value.expectedEventIdentities.slice(0, -1), + auditRevision: 13, + stateProjectionRevision: plan.value.expectedStateProjectionRevision, + }, + }); + expect(rejected).toMatchObject({ ok: false, error: { code: "CONFLICT", locus: "terminalCommitReceipt" } }); + }); +}); diff --git a/tests/unit/t81.test.ts b/tests/unit/t81.test.ts index 1954f6c69..099fe7a74 100644 --- a/tests/unit/t81.test.ts +++ b/tests/unit/t81.test.ts @@ -226,7 +226,8 @@ describe("t81 amadeus-state practices-event — bolt-plan-marker-conflict overri // plus the four Goal Lifecycle events (+4) = 86, // plus QUALITY_REPAIR_TRANSACTION_COMMITTED (#2096, +1) = 87, // plus INTENT_AUTONOMY_TRANSACTION_COMMITTED (#2067, +1) = 88, - // plus AUTO_DECISION_REVIEWED (#2067 review surface, +1) = 89. + // plus AUTO_DECISION_REVIEWED (#2067 review surface, +1) = 89, + // plus INTENT_COMPLETION_TRANSACTION_COMMITTED (#2067 completion seal, +1) = 90. const auditSrc = readFileSync( join(REPO_ROOT, "dist", "claude", ".claude", "tools", "amadeus-audit.ts"), "utf-8", @@ -234,7 +235,7 @@ describe("t81 amadeus-state practices-event — bolt-plan-marker-conflict overri const block = auditSrc.match(/const VALID_EVENT_TYPES = new Set\(\[([\s\S]*?)\]\)/); expect(block).not.toBeNull(); const count = (block ? block[1].match(/"[A-Z0-9_]+"/g) : null)?.length ?? -1; - expect(count).toBe(89); + expect(count).toBe(90); }); // --- Test 4: milestone 8 write-failure path coexists (different Reason value) --- From 9f5d03e5887c5459e1d8bcbcf91a9b0b8bc3a345 Mon Sep 17 00:00:00 2001 From: Junichi Kato Date: Wed, 5 Aug 2026 10:09:12 +0900 Subject: [PATCH 2/6] test(intent-completion): drive the live seam, audit fields, and contract rejections in-process --- tests/.coverage-patch-allowlist.json | 220 +++++++++++++++++++ tests/unit/t427-loop-monitor-runtime.test.ts | 32 +++ tests/unit/t434-intent-completion.test.ts | 29 +++ 3 files changed, 281 insertions(+) diff --git a/tests/.coverage-patch-allowlist.json b/tests/.coverage-patch-allowlist.json index 605a5687d..97e690099 100644 --- a/tests/.coverage-patch-allowlist.json +++ b/tests/.coverage-patch-allowlist.json @@ -6097,5 +6097,225 @@ }, "reason": "CLI flag-validation arm: error() exits the process, so the invalid --choice branch is spawn-only; the valid preview path is driven in-process by t435.", "expiry": "remove when the residual arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "authorize", + "fingerprint": "sha256:be90dd51a48b1a1ffd9a750aa09e5f16cd68f2ef7ef4a1901e92829486df91f1", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "authorize", + "fingerprint": "sha256:56e20d95bf3a9902202afddae3f83587cbed1fbf54b740735fd77881f52826bd", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "", + "fingerprint": "sha256:6791a05eb6a5e904cee5ede99bdd96f9a9ea2b164360361b696a745862e5beec", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "bindRunCommit", + "fingerprint": "sha256:a14c81b40cd028cfc81722b5d418d8c885c87a3919e85e5341be97e02d2fa996", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "nextRunState", + "fingerprint": "sha256:4a71d4418aaa4ce4dfc35b84501e419de441a9a87abdfd55fb9916b59814a310", + "anchorLines": 2, + "targetLines": "1-2" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "nextRunState", + "fingerprint": "sha256:4c07879cf5e060c940e9b7d14245319321f81cb7f1a6b461b307ab9726088e4f", + "anchorLines": 2, + "targetLines": "1-2" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "nextRunState", + "fingerprint": "sha256:93c44d88c469930f8322a89a5cbb61f9e0e088ebe616a082f73332a70c9c634c", + "anchorLines": 3, + "targetLines": "1-3" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "bindTransitionCommit", + "fingerprint": "sha256:d1fa8234d347e792dcc466b0183df06e8899a2a467232a8dc75bf0e6d9f54c5e", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "bindDispatchClaimCommit", + "fingerprint": "sha256:c9697fe79467c28b9cf183ad246f7b279fc94f534977634a8b6ee422ab4c420a", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "dispatch", + "fingerprint": "sha256:094c5377128ba15ad0207d39c3f04a9ba1ef42ef226943003d8cbcdd1975207b", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "dispatch", + "fingerprint": "sha256:0f9a5c15b4a16d11de4507c38c8e02295616e318b5baddcec9fe089842ef6d5e", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "validate", + "fingerprint": "sha256:f878867aae223c1781bf9f52037fe36117d7bf201a5f0240e9ea763675e8de47", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "", + "fingerprint": "sha256:e8fb0340fbc6d1d1f269f873c541b7b0f83b468afee4ce2ceaddefa207bc657d", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "evaluate", + "fingerprint": "sha256:8217a86501f9405a74df10f1a68368f1fb94852f0dde07bfb0deabfae4c3947b", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "planTerminalCommit", + "fingerprint": "sha256:0b48e4b4a14154a8410cf50df721e8f03ebd67b64fbd8ae438eec9ab2e4fb6fe", + "anchorLines": 3, + "targetLines": "2" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "planTerminalCommit", + "fingerprint": "sha256:566d24d39e935cafeec69578adf47de3256ef9bfd2ff45f807a9297fe4686ebf", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "planTerminalCommit", + "fingerprint": "sha256:8facb76af6f40556cb8d26efa96af750d426bf0331c24cc0f4fcd99a5a15f4f7", + "anchorLines": 3, + "targetLines": "2" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "commit", + "fingerprint": "sha256:36646db9409e11195dc1bd165414df268be7585a5602fa058adf181c264786df", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "awaitingLiveCapability", + "fingerprint": "sha256:a869ca6251e309877bcc535067710977194ac48b62cdeb5495be7117e294b1b1", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "parseCompletionEvidencePayload", + "fingerprint": "sha256:413a2c93374cc0bcdab8ebb02954865cab94e8ac359ae10a063421640003da73", + "anchorLines": 2, + "targetLines": "1-2" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" } ] diff --git a/tests/unit/t427-loop-monitor-runtime.test.ts b/tests/unit/t427-loop-monitor-runtime.test.ts index 3d7a4d28a..1eac14abc 100644 --- a/tests/unit/t427-loop-monitor-runtime.test.ts +++ b/tests/unit/t427-loop-monitor-runtime.test.ts @@ -572,3 +572,35 @@ describe("Loop Monitor latch", () => { expect(coordinator.readProjection(partition).latch).not.toBeNull(); }); }); + +describe("live smoke authorization seam", () => { + test("records a credential-attested authorization as a partition event", () => { + const routeGraph = graph(); + const repository = createMemoryLoopMonitorRepository(); + const coordinator = createLoopMonitorCoordinator({ graph: routeGraph, repository }); + const partition: LoopMonitorPartition = { + intentUuid: "intent-1", + monitorId: "quality-repair", + stageInstanceId: "stage-1", + graphRevision: routeGraph.graphRevision, + }; + const port = { + authorize: (request: { readonly intentUuid: string; readonly monitorId: string; readonly scopeDigest: string }) => ({ + authorized: true as const, + authorizationId: `live-auth-${request.monitorId}`, + actorId: "credential-attested-human", + }), + }; + const authorized = coordinator.authorizeLiveSmoke(partition, "sha256:scope", port); + expect(authorized.kind).toBe("authorized"); + if (authorized.kind !== "authorized") return; + expect(authorized.receipt.eventSetId.length).toBeGreaterThan(0); + // Denied port fails closed without an event. + expect(coordinator.authorizeLiveSmoke(partition, "sha256:scope", { + authorize: () => ({ authorized: false as const, reason: "credential-missing" }), + })).toEqual({ kind: "CONFLICT", reason: "credential-missing" }); + // Unknown partition never reaches the port. + expect(coordinator.authorizeLiveSmoke({ ...partition, monitorId: "unknown-monitor" }, "sha256:scope", port)) + .toEqual({ kind: "CONFLICT", reason: "partition-not-in-compiled-graph" }); + }); +}); diff --git a/tests/unit/t434-intent-completion.test.ts b/tests/unit/t434-intent-completion.test.ts index 76349e077..a55064cd0 100644 --- a/tests/unit/t434-intent-completion.test.ts +++ b/tests/unit/t434-intent-completion.test.ts @@ -14,6 +14,7 @@ import { createMemoryIntentCompletionLedger, parseCompletionEvidencePayload, planTerminalCommit, + terminalTransactionAuditFields, resolveRequiredCompletionCohort, validateHarnessRegistry, type AuditCommitReceipt, @@ -216,6 +217,23 @@ async function buildFiveHarnessEvidence() { } describe("five-harness completion cohort", () => { + test("rejects duplicate harness ids and an empty completion cohort", () => { + const registry = validateHarnessRegistry(); + if (!registry.ok) throw new Error(registry.error.detail); + const first = registry.value.descriptors[0]!; + expect(validateHarnessRegistry([first, first])).toMatchObject({ + ok: false, + error: { code: "MALFORMED", locus: "registry" }, + }); + const noLive = registry.value.descriptors.map((descriptor) => ({ ...descriptor, autonomyLive: false })); + const noLiveRegistry = validateHarnessRegistry(noLive); + if (!noLiveRegistry.ok) throw new Error(noLiveRegistry.error.detail); + expect(resolveRequiredCompletionCohort(noLiveRegistry.value)).toMatchObject({ + ok: false, + error: { code: "MALFORMED", locus: "cohort" }, + }); + }); + test("registry resolves the exact current five while retaining Kiro rows", () => { const registry = validateHarnessRegistry(); expect(registry.ok).toBe(true); @@ -363,6 +381,10 @@ describe("receipt validation and terminal transaction", () => { if (!isCompleteEvaluation(evaluation.value)) throw new Error("expected complete five-harness evaluation"); expect(evaluation.value.check.evidence.receiptIds).toEqual(fixture.cohort.harnessIds.map((id) => `receipt-${id}`)); expect(parseCompletionEvidencePayload(evaluation.value.audit[0]).ok).toBe(true); + expect(parseCompletionEvidencePayload({ + ...evaluation.value.audit[0], + payloadDigest: `sha256:${"f".repeat(64)}`, + })).toMatchObject({ ok: false, error: { code: "CONFLICT", locus: "completionEvidencePayload" } }); const plan = planTerminalCommit({ current: fullProjection(), evaluation: evaluation.value }); expect(plan.ok).toBe(true); @@ -373,6 +395,13 @@ describe("receipt validation and terminal transaction", () => { "WORKFLOW_STATE_CLEARED", "WORKFLOW_COMPLETED", ]); + const auditFields = terminalTransactionAuditFields(plan.value); + expect(auditFields["Intent Uuid"]).toBe(INTENT); + expect(auditFields["Transaction Id"]).toBe(plan.value.transaction.transactionId); + expect(auditFields["Completion Seal Digest"]).toBe(plan.value.completionSealDigest); + expect(JSON.parse(auditFields.Transaction!)).toMatchObject({ + eventType: "INTENT_COMPLETION_TRANSACTION_COMMITTED", + }); expect(plan.value.terminalProjection.workflowExecutionState).toBeNull(); expect(plan.value.terminalProjection.currentGrant).toBeNull(); expect(plan.value.terminalProjection.terminalGrantHistory.at(-1)?.state).toBe("completed"); From 3a5a85e598f3e28f569b706bd2b17b3da54db627 Mon Sep 17 00:00:00 2001 From: Junichi Kato Date: Wed, 5 Aug 2026 10:38:30 +0900 Subject: [PATCH 3/6] fix(intent-completion): address the PR #2234 review batch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - degradation observations bind to AUTO_DECIDED: the canonical vocabulary carries no AUTO_DECISION_DEGRADED type — degradation is decision metadata, so both election outcomes validate against the same row - evaluate() bounds the evidence by the requested validation identity set and replaces the classified-set cast with an explicit guard - maxDispatches drives the redispatch bound; the ledger snapshot joins the module-wide string "1" schema version; exactlyKeys sorts once; the unused event constant folds into the emission literal the t48 drift matcher pins - audit-format section heading counts 5 events; the five-harness projection also compares amadeus-audit.ts; the live seam pins receipt revision/digest shape and cohort-wide equality; t427 proves the authorization port is never consulted for an unknown partition; t434 drives the completed reconcile path and the forged idempotency-key rejection --- .../knowledge/amadeus-shared/audit-format.md | 2 +- .../core/tools/amadeus-intent-completion.ts | 39 +++++++++----- ...ive-harness-projection.integration.test.ts | 2 +- ...t-completion-live-seam.integration.test.ts | 13 ++++- tests/unit/t427-loop-monitor-runtime.test.ts | 10 +++- tests/unit/t434-intent-completion.test.ts | 54 ++++++++++++++++--- 6 files changed, 95 insertions(+), 25 deletions(-) diff --git a/packages/framework/core/knowledge/amadeus-shared/audit-format.md b/packages/framework/core/knowledge/amadeus-shared/audit-format.md index 2fd599fa9..02c7b5159 100644 --- a/packages/framework/core/knowledge/amadeus-shared/audit-format.md +++ b/packages/framework/core/knowledge/amadeus-shared/audit-format.md @@ -247,7 +247,7 @@ Emitted by stage-protocol §13 (Learnings Ritual). The runtime-graph compile emi | `RULE_LEARNED` | The learning gate persisted a kept learning as a practice line under the routed heading in `{project,team}.md` | Stage, Candidate-ID, Destination, Heading, Source | — | `tools/amadeus-learnings.ts persist` | | `SENSOR_PROPOSED` | The learning gate scaffolded a project-tier sensor manifest and bound it to the originating stage's `sensors:` frontmatter | Stage, Candidate-ID, Sensor ID, Manifest path, Matches, Destinations, Source | — | `tools/amadeus-learnings.ts persist` | -### Loop Monitor, Quality Repair, and Intent Autonomy (4 events) +### Loop Monitor, Quality Repair, and Intent Autonomy (5 events) The event set is the atomic canonical stream for delivery observation, cycle trigger, Judge reservation/result, closed route application, and latch transitions. The per-clone Replay Index is a repairable secondary projection and never replaces this audit source of truth. diff --git a/packages/framework/core/tools/amadeus-intent-completion.ts b/packages/framework/core/tools/amadeus-intent-completion.ts index 76533fedd..d74d7c33d 100644 --- a/packages/framework/core/tools/amadeus-intent-completion.ts +++ b/packages/framework/core/tools/amadeus-intent-completion.ts @@ -27,7 +27,6 @@ import type { ReviewIntentSeed } from "./amadeus-autonomy-review.ts"; export type StableId = string; export type Sha256Digest = string; export type CompletionHarnessId = HarnessDescriptor["id"]; -export const INTENT_COMPLETION_TRANSACTION_COMMITTED_EVENT = "INTENT_COMPLETION_TRANSACTION_COMMITTED"; // Live verification is optional evidence. The production workflow completion // path must never import this module or wait for a receipt cohort. export const CORE_INTENT_COMPLETION_REQUIRES_LIVE_RECEIPTS = false as const; @@ -69,8 +68,9 @@ function validRevision(revision: LiveScenarioRevision): boolean { function exactlyKeys(value: Record, expected: readonly string[]): boolean { const observed = Object.keys(value).sort(compareUtf8); - return observed.length === expected.length && - observed.every((field, index) => field === [...expected].sort(compareUtf8)[index]); + const wanted = [...expected].sort(compareUtf8); + return observed.length === wanted.length && + observed.every((field, index) => field === wanted[index]); } export interface AuditEventPlan { @@ -538,8 +538,9 @@ function nextRunState( return success({ next: { ...run, status: "started", dispatchesAuthorized: 1 }, receipt: null, proofDigest: proof.value.proofDigest }); } - if (run.dispatchesAuthorized === 1 && (run.status === "started" || run.status === "dispatch-claimed")) { - return success({ next: { ...run, status: "redispatch-authorized", dispatchesAuthorized: 2 }, receipt: null, + if (run.dispatchesAuthorized < run.maxDispatches && (run.status === "started" || run.status === "dispatch-claimed")) { + const nextAuthorized = (run.dispatchesAuthorized + 1) as IntentLiveRunReservation["dispatchesAuthorized"]; + return success({ next: { ...run, status: "redispatch-authorized", dispatchesAuthorized: nextAuthorized }, receipt: null, proofDigest: proof.value.proofDigest }); } return success({ next: { ...run, status: "incomplete" }, receipt: null, proofDigest: proof.value.proofDigest }); @@ -759,10 +760,13 @@ function canonicalObservationExists( receipt: RawIntentLiveReceipt & { readonly observation: LiveObservation }, ): boolean { const observation = receipt.observation; - const decisionType = observation.electionOutcome === "elected" ? "AUTO_DECIDED" : "AUTO_DECISION_DEGRADED"; + // A loud degradation is still recorded as AUTO_DECIDED — the canonical + // vocabulary carries no AUTO_DECISION_DEGRADED type; degradation lives in the + // decision's degradedCapability metadata, so both election outcomes bind to + // the same canonical decision row. return observedEvent(snapshot, "LOOP_JUDGE_STARTED", observation.judgeInvocationId, receipt.traceId) && observedEvent(snapshot, "LOOP_JUDGE_RESULT_OBSERVED", observation.judgeInvocationId, receipt.traceId) && - observedEvent(snapshot, decisionType, observation.electionDecisionId, receipt.traceId); + observedEvent(snapshot, "AUTO_DECIDED", observation.electionDecisionId, receipt.traceId); } export interface IntentLiveReceiptValidator { @@ -966,6 +970,12 @@ export function createIntentCompletionEvaluator(deps: { validationEventIdentities: input.validationEventIdentities, }); if (!snapshot.ok) return snapshot; + // The requested identity set BOUNDS the evidence: a reader that returns + // receipts outside it cannot widen what the completion seal binds. + const requested = new Set(input.validationEventIdentities); + if (snapshot.value.receipts.some((receipt) => !requested.has(receipt.validationEventIdentity))) { + return failure("CONFLICT", "validationSet", "reader returned a receipt outside the requested validation set"); + } const classified = classifyReceiptSet(snapshot.value.receipts, input.intentUuid, input.cohort, input.revision); if (classified.missing.length > 0 || classified.rejected.length > 0) return success({ check: { @@ -980,9 +990,14 @@ export function createIntentCompletionEvaluator(deps: { sourceAuditRevision: snapshot.value.auditRevision, sourceStateProjectionRevision: snapshot.value.stateProjectionRevision, }); - const ordered = input.cohort.harnessIds.map( - (harnessId) => classified.byHarness.get(harnessId)?.[0] as CommittedValidatedIntentLiveReceipt, - ); + const ordered: CommittedValidatedIntentLiveReceipt[] = []; + for (const harnessId of input.cohort.harnessIds) { + const receipt = classified.byHarness.get(harnessId)?.[0]; + if (receipt === undefined) { + return failure("ILLEGAL_STATE", "completionEvidence", `classified set lost harness ${harnessId}`); + } + ordered.push(receipt); + } const evidenceBase = { schemaVersion: "1" as const, intentUuid: input.intentUuid, @@ -1234,7 +1249,7 @@ export function acceptTerminalCommit(input: { } export interface IntentCompletionLedgerSnapshot { - readonly schemaVersion: 1; + readonly schemaVersion: "1"; readonly auditRevision: number; readonly stateProjectionRevision: number; readonly committedTransactions: readonly { @@ -1291,7 +1306,7 @@ export function createMemoryIntentCompletionLedger( }, exportSnapshot() { const value = { - schemaVersion: 1 as const, + schemaVersion: "1" as const, auditRevision, stateProjectionRevision, committedTransactions: [...transactions].sort(([left], [right]) => compareUtf8(left, right)).map( diff --git a/tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts b/tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts index f469e4efb..d5fb5bbd9 100644 --- a/tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts +++ b/tests/integration/t434-intent-completion-five-harness-projection.integration.test.ts @@ -14,7 +14,7 @@ const HARNESSES = [ ["opencode", ".opencode"], ["kimi", ".kimi-code"], ] as const; -const FILES = ["amadeus-harness-registry.ts", "amadeus-intent-completion.ts"] as const; +const FILES = ["amadeus-audit.ts", "amadeus-harness-registry.ts", "amadeus-intent-completion.ts"] as const; describe("Intent completion current five-harness projection", () => { for (const [harness, directory] of HARNESSES) { diff --git a/tests/integration/t434-intent-completion-live-seam.integration.test.ts b/tests/integration/t434-intent-completion-live-seam.integration.test.ts index 6910b41dc..691c0d794 100644 --- a/tests/integration/t434-intent-completion-live-seam.integration.test.ts +++ b/tests/integration/t434-intent-completion-live-seam.integration.test.ts @@ -54,8 +54,17 @@ describe("opt-in five-harness credential seam", () => { expect(receipt.success).toBe(true); expect(receipt.judge_invoked).toBe(true); expect(["solo-election", "loud-degradation"]).toContain(String(receipt.decision_path)); - expect(typeof receipt.implementation_revision).toBe("string"); - expect(typeof receipt.package_digest).toBe("string"); + // The registry mints no canonical per-release revision values, so the + // seam binds SHAPE here (Core's receipt validator pins the exact values + // to the committed authorization): a git-object revision and a sha256 + // package digest, identical across the cohort because all five ship the + // same bundle. + expect(String(receipt.implementation_revision)).toMatch(/^[0-9a-f]{40}$/); + expect(String(receipt.package_digest)).toMatch(/^sha256:[0-9a-f]{64}$/); } + const revisions = new Set(receipts.map(({ receipt }) => String(receipt.implementation_revision))); + const digests = new Set(receipts.map(({ receipt }) => String(receipt.package_digest))); + expect(revisions.size).toBe(1); + expect(digests.size).toBe(1); }); }); diff --git a/tests/unit/t427-loop-monitor-runtime.test.ts b/tests/unit/t427-loop-monitor-runtime.test.ts index 1eac14abc..4223bf262 100644 --- a/tests/unit/t427-loop-monitor-runtime.test.ts +++ b/tests/unit/t427-loop-monitor-runtime.test.ts @@ -600,7 +600,15 @@ describe("live smoke authorization seam", () => { authorize: () => ({ authorized: false as const, reason: "credential-missing" }), })).toEqual({ kind: "CONFLICT", reason: "credential-missing" }); // Unknown partition never reaches the port. - expect(coordinator.authorizeLiveSmoke({ ...partition, monitorId: "unknown-monitor" }, "sha256:scope", port)) + let unknownPartitionCalls = 0; + const countingPort = { + authorize: () => { + unknownPartitionCalls++; + return { authorized: true as const, authorizationId: "never", actorId: "never" }; + }, + }; + expect(coordinator.authorizeLiveSmoke({ ...partition, monitorId: "unknown-monitor" }, "sha256:scope", countingPort)) .toEqual({ kind: "CONFLICT", reason: "partition-not-in-compiled-graph" }); + expect(unknownPartitionCalls).toBe(0); }); }); diff --git a/tests/unit/t434-intent-completion.test.ts b/tests/unit/t434-intent-completion.test.ts index a55064cd0..227c26c3b 100644 --- a/tests/unit/t434-intent-completion.test.ts +++ b/tests/unit/t434-intent-completion.test.ts @@ -279,6 +279,11 @@ describe("receipt validation and terminal transaction", () => { let snapshot: CanonicalLiveRunSnapshot | null = null; let nativeStarts = 0; let dispatchCalls = 0; + let forgeDispatchKey = false; + let reconcileResult: Awaited[0]["nativePort"]["reconcile"]>> = { ok: true, value: { + kind: "attested-no-effect", + proofDigest: autonomyDigest("no-effect-proof"), + } }; const coordinator = createIntentLiveRunCoordinator({ runReader: { readRunSnapshot: ({ intentUuid, runId }) => snapshot !== null && @@ -290,17 +295,13 @@ describe("receipt validation and terminal transaction", () => { verify: ({ reconciliation }) => ({ ok: true, value: { proofDigest: reconciliation.proofDigest } }), }, nativePort: { - reconcile: async () => ({ ok: true, value: { - kind: "attested-no-effect", - proofDigest: autonomyDigest("no-effect-proof"), - } }), + reconcile: async () => reconcileResult, dispatch: async (claimed) => { dispatchCalls += 1; if (dispatchCalls === 1) nativeStarts += 1; - const key = canonicalContractValueDigest("intent-live-native-dispatch", { - operationReference: claimed.operationReference, - attempt: claimed.attempt, - }); + const key = canonicalContractValueDigest("intent-live-native-dispatch", forgeDispatchKey + ? { operationReference: "forged-operation", attempt: claimed.attempt } + : { operationReference: claimed.operationReference, attempt: claimed.attempt }); if (!key.ok) return key; return { ok: true, value: { schemaVersion: "1", @@ -366,6 +367,43 @@ describe("receipt validation and terminal transaction", () => { expect((await coordinator.dispatch(committedClaim.value))).toMatchObject({ ok: true, value: { outcome: "started" } }); expect((await coordinator.dispatch(committedClaim.value))).toMatchObject({ ok: true, value: { outcome: "attached" } }); expect(nativeStarts).toBe(1); + + // A native receipt whose idempotency key does not derive from THIS claim is + // refused — the retry seam cannot attach a foreign operation. + forgeDispatchKey = true; + expect(await coordinator.dispatch(committedClaim.value)).toMatchObject({ + ok: false, + error: { code: "CONFLICT", locus: "nativeDispatchReceipt" }, + }); + forgeDispatchKey = false; + + // A native reconcile that reports the operation completed transitions the + // run to its terminal completed state and surfaces the receipt. + reconcileResult = { ok: true, value: { + kind: "completed", + receipt: { + schemaVersion: "1", + receiptId: "receipt-native-completed", + intentUuid: INTENT, + harnessId: "claude", + authorizationId: snapshot.authorization.authorizationId, + authorizationEventIdentity: snapshot.authorization.authorizationEventIdentity, + authorizationCommitTransactionId: snapshot.authorization.commitReceipt.transactionId, + revision: snapshot.run.revision, + environmentId: "environment-native-completed", + traceId: "trace-native-completed", + spanId: "span-native-completed", + attestationDigest: autonomyDigest("native-completed-attestation"), + outcome: "passed", + observation: null, + }, + proofDigest: autonomyDigest("native-completed-proof"), + } }; + const completed = await coordinator.planNext({ intentUuid: INTENT, runId: claimedRun.runId }); + expect(completed.ok).toBe(true); + if (!completed.ok) throw new Error(completed.error.detail); + expect(completed.value.next.status).toBe("completed"); + expect(completed.value.receipt?.receiptId).toBe("receipt-native-completed"); }); test("all five exact receipts atomically complete full grant and clear workflow", async () => { From 0c8fdb08819f2b572dffc2feef93d4f72d1deaee Mon Sep 17 00:00:00 2001 From: Junichi Kato Date: Wed, 5 Aug 2026 10:47:25 +0900 Subject: [PATCH 4/6] test(coverage): drop intent-completion allowlist entries invalidated by review fixes --- tests/.coverage-patch-allowlist.json | 220 --------------------------- 1 file changed, 220 deletions(-) diff --git a/tests/.coverage-patch-allowlist.json b/tests/.coverage-patch-allowlist.json index 97e690099..605a5687d 100644 --- a/tests/.coverage-patch-allowlist.json +++ b/tests/.coverage-patch-allowlist.json @@ -6097,225 +6097,5 @@ }, "reason": "CLI flag-validation arm: error() exits the process, so the invalid --choice branch is spawn-only; the valid preview path is driven in-process by t435.", "expiry": "remove when the residual arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "authorize", - "fingerprint": "sha256:be90dd51a48b1a1ffd9a750aa09e5f16cd68f2ef7ef4a1901e92829486df91f1", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "authorize", - "fingerprint": "sha256:56e20d95bf3a9902202afddae3f83587cbed1fbf54b740735fd77881f52826bd", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "", - "fingerprint": "sha256:6791a05eb6a5e904cee5ede99bdd96f9a9ea2b164360361b696a745862e5beec", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "bindRunCommit", - "fingerprint": "sha256:a14c81b40cd028cfc81722b5d418d8c885c87a3919e85e5341be97e02d2fa996", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "nextRunState", - "fingerprint": "sha256:4a71d4418aaa4ce4dfc35b84501e419de441a9a87abdfd55fb9916b59814a310", - "anchorLines": 2, - "targetLines": "1-2" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "nextRunState", - "fingerprint": "sha256:4c07879cf5e060c940e9b7d14245319321f81cb7f1a6b461b307ab9726088e4f", - "anchorLines": 2, - "targetLines": "1-2" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "nextRunState", - "fingerprint": "sha256:93c44d88c469930f8322a89a5cbb61f9e0e088ebe616a082f73332a70c9c634c", - "anchorLines": 3, - "targetLines": "1-3" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "bindTransitionCommit", - "fingerprint": "sha256:d1fa8234d347e792dcc466b0183df06e8899a2a467232a8dc75bf0e6d9f54c5e", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "bindDispatchClaimCommit", - "fingerprint": "sha256:c9697fe79467c28b9cf183ad246f7b279fc94f534977634a8b6ee422ab4c420a", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "dispatch", - "fingerprint": "sha256:094c5377128ba15ad0207d39c3f04a9ba1ef42ef226943003d8cbcdd1975207b", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "dispatch", - "fingerprint": "sha256:0f9a5c15b4a16d11de4507c38c8e02295616e318b5baddcec9fe089842ef6d5e", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "validate", - "fingerprint": "sha256:f878867aae223c1781bf9f52037fe36117d7bf201a5f0240e9ea763675e8de47", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "", - "fingerprint": "sha256:e8fb0340fbc6d1d1f269f873c541b7b0f83b468afee4ce2ceaddefa207bc657d", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "evaluate", - "fingerprint": "sha256:8217a86501f9405a74df10f1a68368f1fb94852f0dde07bfb0deabfae4c3947b", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "planTerminalCommit", - "fingerprint": "sha256:0b48e4b4a14154a8410cf50df721e8f03ebd67b64fbd8ae438eec9ab2e4fb6fe", - "anchorLines": 3, - "targetLines": "2" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "planTerminalCommit", - "fingerprint": "sha256:566d24d39e935cafeec69578adf47de3256ef9bfd2ff45f807a9297fe4686ebf", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "planTerminalCommit", - "fingerprint": "sha256:8facb76af6f40556cb8d26efa96af750d426bf0331c24cc0f4fcd99a5a15f4f7", - "anchorLines": 3, - "targetLines": "2" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "commit", - "fingerprint": "sha256:36646db9409e11195dc1bd165414df268be7585a5602fa058adf181c264786df", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "awaitingLiveCapability", - "fingerprint": "sha256:a869ca6251e309877bcc535067710977194ac48b62cdeb5495be7117e294b1b1", - "anchorLines": 1, - "targetLines": "1" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" - }, - { - "file": "packages/framework/core/tools/amadeus-intent-completion.ts", - "selector": { - "function": "parseCompletionEvidencePayload", - "fingerprint": "sha256:413a2c93374cc0bcdab8ebb02954865cab94e8ac359ae10a063421640003da73", - "anchorLines": 2, - "targetLines": "1-2" - }, - "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, evidence payload, terminal receipt, replay — are pinned in-process by t434.", - "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" } ] From 85e8c603fde67014662f04929b9184e9ae2e9307 Mon Sep 17 00:00:00 2001 From: Junichi Kato Date: Wed, 5 Aug 2026 10:49:01 +0900 Subject: [PATCH 5/6] test(intent-completion): drive redispatch, incomplete, and validation-set bounds; re-mint residual allowlist --- tests/.coverage-patch-allowlist.json | 198 ++++++++++++++++++++++ tests/unit/t434-intent-completion.test.ts | 28 +++ 2 files changed, 226 insertions(+) diff --git a/tests/.coverage-patch-allowlist.json b/tests/.coverage-patch-allowlist.json index 605a5687d..10af8be7c 100644 --- a/tests/.coverage-patch-allowlist.json +++ b/tests/.coverage-patch-allowlist.json @@ -6097,5 +6097,203 @@ }, "reason": "CLI flag-validation arm: error() exits the process, so the invalid --choice branch is spawn-only; the valid preview path is driven in-process by t435.", "expiry": "remove when the residual arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "authorize", + "fingerprint": "sha256:be90dd51a48b1a1ffd9a750aa09e5f16cd68f2ef7ef4a1901e92829486df91f1", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "authorize", + "fingerprint": "sha256:56e20d95bf3a9902202afddae3f83587cbed1fbf54b740735fd77881f52826bd", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "", + "fingerprint": "sha256:6791a05eb6a5e904cee5ede99bdd96f9a9ea2b164360361b696a745862e5beec", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "bindRunCommit", + "fingerprint": "sha256:a14c81b40cd028cfc81722b5d418d8c885c87a3919e85e5341be97e02d2fa996", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "bindTransitionCommit", + "fingerprint": "sha256:d1fa8234d347e792dcc466b0183df06e8899a2a467232a8dc75bf0e6d9f54c5e", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "bindDispatchClaimCommit", + "fingerprint": "sha256:c9697fe79467c28b9cf183ad246f7b279fc94f534977634a8b6ee422ab4c420a", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "dispatch", + "fingerprint": "sha256:094c5377128ba15ad0207d39c3f04a9ba1ef42ef226943003d8cbcdd1975207b", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "dispatch", + "fingerprint": "sha256:0f9a5c15b4a16d11de4507c38c8e02295616e318b5baddcec9fe089842ef6d5e", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "validate", + "fingerprint": "sha256:f878867aae223c1781bf9f52037fe36117d7bf201a5f0240e9ea763675e8de47", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "", + "fingerprint": "sha256:e8fb0340fbc6d1d1f269f873c541b7b0f83b468afee4ce2ceaddefa207bc657d", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "evaluate", + "fingerprint": "sha256:8217a86501f9405a74df10f1a68368f1fb94852f0dde07bfb0deabfae4c3947b", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "evaluate", + "fingerprint": "sha256:e1274a0bbe2f4424b0d938e4cb0a67883355ccadbef927f2d2bbfefdcb80165e", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "planTerminalCommit", + "fingerprint": "sha256:0b48e4b4a14154a8410cf50df721e8f03ebd67b64fbd8ae438eec9ab2e4fb6fe", + "anchorLines": 3, + "targetLines": "2" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "planTerminalCommit", + "fingerprint": "sha256:566d24d39e935cafeec69578adf47de3256ef9bfd2ff45f807a9297fe4686ebf", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "planTerminalCommit", + "fingerprint": "sha256:8facb76af6f40556cb8d26efa96af750d426bf0331c24cc0f4fcd99a5a15f4f7", + "anchorLines": 3, + "targetLines": "2" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "commit", + "fingerprint": "sha256:36646db9409e11195dc1bd165414df268be7585a5602fa058adf181c264786df", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "awaitingLiveCapability", + "fingerprint": "sha256:a869ca6251e309877bcc535067710977194ac48b62cdeb5495be7117e294b1b1", + "anchorLines": 1, + "targetLines": "1" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" + }, + { + "file": "packages/framework/core/tools/amadeus-intent-completion.ts", + "selector": { + "function": "parseCompletionEvidencePayload", + "fingerprint": "sha256:413a2c93374cc0bcdab8ebb02954865cab94e8ac359ae10a063421640003da73", + "anchorLines": 2, + "targetLines": "1-2" + }, + "reason": "Deep fail-closed arm of the live conformance pipeline (forged/partial multi-stage state that the public seams cannot construct); the reachable contract rejections — registry, cohort, validation-set bound, evidence payload, run transitions, terminal receipt, replay — are pinned in-process by t434.", + "expiry": "remove when the arm gains a deterministic in-process driver, or Bun merges child-process coverage" } ] diff --git a/tests/unit/t434-intent-completion.test.ts b/tests/unit/t434-intent-completion.test.ts index 227c26c3b..dd237c387 100644 --- a/tests/unit/t434-intent-completion.test.ts +++ b/tests/unit/t434-intent-completion.test.ts @@ -368,6 +368,22 @@ describe("receipt validation and terminal transaction", () => { expect((await coordinator.dispatch(committedClaim.value))).toMatchObject({ ok: true, value: { outcome: "attached" } }); expect(nativeStarts).toBe(1); + // A no-effect reconcile on a claimed run re-authorizes exactly one more + // dispatch, bounded by maxDispatches. + const redispatch = await coordinator.planNext({ intentUuid: INTENT, runId: claimedRun.runId }); + expect(redispatch.ok).toBe(true); + if (!redispatch.ok) throw new Error(redispatch.error.detail); + expect(redispatch.value.next.status).toBe("redispatch-authorized"); + expect(redispatch.value.next.dispatchesAuthorized).toBe(2); + + // An effect-possible reconcile cannot attest a clean retry: the run closes + // as incomplete instead of re-dispatching. + reconcileResult = { ok: true, value: { kind: "effect-possible", proofDigest: null } }; + const incomplete = await coordinator.planNext({ intentUuid: INTENT, runId: claimedRun.runId }); + expect(incomplete.ok).toBe(true); + if (!incomplete.ok) throw new Error(incomplete.error.detail); + expect(incomplete.value.next.status).toBe("incomplete"); + // A native receipt whose idempotency key does not derive from THIS claim is // refused — the retry seam cannot attach a foreign operation. forgeDispatchKey = true; @@ -458,6 +474,18 @@ describe("receipt validation and terminal transaction", () => { })).not.toThrow(); }); + test("a reader receipt outside the requested validation set is refused", async () => { + const fixture = await buildFiveHarnessEvidence(); + const identities = fixture.validations.map((receipt) => receipt.validationEventIdentity); + const evaluation = fixture.evaluator.evaluate({ + intentUuid: INTENT, + cohort: fixture.cohort, + revision: fixture.revision, + validationEventIdentities: identities.slice(0, identities.length - 1), + }); + expect(evaluation).toMatchObject({ ok: false, error: { code: "CONFLICT", locus: "validationSet" } }); + }); + test("missing, duplicate, skip, forged, and revision mismatch never create completion evidence", async () => { const fixture = await buildFiveHarnessEvidence(); const missingReader = createIntentCompletionEvaluator({ From 64f3cbe774a2c7a406929a92583cb830a0a3ae10 Mon Sep 17 00:00:00 2001 From: Junichi Kato Date: Wed, 5 Aug 2026 11:12:11 +0900 Subject: [PATCH 6/6] fix(intent-completion): refuse unknown ledger snapshot schema versions before the digest check --- .../core/tools/amadeus-intent-completion.ts | 6 ++++++ tests/unit/t434-intent-completion.test.ts | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/framework/core/tools/amadeus-intent-completion.ts b/packages/framework/core/tools/amadeus-intent-completion.ts index d74d7c33d..b48c3751f 100644 --- a/packages/framework/core/tools/amadeus-intent-completion.ts +++ b/packages/framework/core/tools/amadeus-intent-completion.ts @@ -1272,6 +1272,12 @@ export function createMemoryIntentCompletionLedger( let stateProjectionRevision = input.stateProjectionRevision; const transactions = new Map(); if (input.snapshot !== undefined) { + // A digest proves integrity, not compatibility: an untyped caller can hand + // over a well-digested snapshot of a FUTURE schema, and replaying it as + // "1" would silently re-emit it under the wrong version. Refuse first. + if (input.snapshot.schemaVersion !== "1") { + throw new Error("unsupported-intent-completion-ledger-snapshot-schema"); + } const { digest: snapshotDigest, ...value } = input.snapshot; const observed = digest("intent-completion-ledger", value); if (!observed.ok || observed.value !== snapshotDigest) throw new Error("invalid-intent-completion-ledger-snapshot"); diff --git a/tests/unit/t434-intent-completion.test.ts b/tests/unit/t434-intent-completion.test.ts index dd237c387..41fac6593 100644 --- a/tests/unit/t434-intent-completion.test.ts +++ b/tests/unit/t434-intent-completion.test.ts @@ -469,6 +469,18 @@ describe("receipt validation and terminal transaction", () => { const restored = createMemoryIntentCompletionLedger({ auditRevision: 0, stateProjectionRevision: 0, snapshot: ledger.exportSnapshot() }); expect(restored.commit(plan.value)).toEqual(committed); + + // A well-digested snapshot of an UNKNOWN schema version is refused before + // the digest check can bless it as "1". + const exported = ledger.exportSnapshot(); + const { digest: _oldDigest, ...futureValue } = { ...exported, schemaVersion: "2" }; + const futureDigest = canonicalContractValueDigest("intent-completion-ledger", futureValue); + if (!futureDigest.ok) throw new Error(futureDigest.error.detail); + expect(() => createMemoryIntentCompletionLedger({ + auditRevision: 0, + stateProjectionRevision: 0, + snapshot: { ...futureValue, digest: futureDigest.value } as unknown as ReturnType, + })).toThrow("unsupported-intent-completion-ledger-snapshot-schema"); expect(() => createMemoryAutonomyReviewService({ intents: [completedReviewSeed(plan.value, 14)], })).not.toThrow();