From f4df7be755569f1c5f4c2ab6925fe0be62cad066 Mon Sep 17 00:00:00 2001 From: Joe Feser Date: Fri, 4 Sep 2026 01:32:55 -0500 Subject: [PATCH 1/6] Define bounded local owner continuation candidate --- .../wits-v0/local-owner-profile-fixtures.json | 31 +++ .../wits-v0/local-owner-profile.md | 197 ++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 docs/source-packets/wits-v0/local-owner-profile-fixtures.json create mode 100644 docs/source-packets/wits-v0/local-owner-profile.md diff --git a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json new file mode 100644 index 0000000..4a6ac19 --- /dev/null +++ b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json @@ -0,0 +1,31 @@ +{ + "kind": "hacp.candidate_fixture_inventory", + "profileId": "org.hacp.local-owner-continuation", + "profileVersion": "0.1-candidate", + "status": "required_observations_not_completed_proof", + "cases": [ + { "id": "authenticated-happy-path", "expected": "one durable claim and start intent, one dry-run observation, separate result" }, + { "id": "missing-or-wrong-authentication", "expected": "access denied before protected read, mutation or observation" }, + { "id": "caller-forged-issuer-or-actor", "expected": "rejected, configured identity cannot be overridden" }, + { "id": "two-overlapping-claims", "expected": "one issuer/decision slot, one winner, competing successor denied" }, + { "id": "same-id-different-issuer", "expected": "separate configured issuer slots; no cross-issuer access or caller reassignment" }, + { "id": "changed-binding-replay", "expected": "conflict for changed digest/request/action/expiry/profile/successor on both replay paths" }, + { "id": "exact-claim-retry", "expected": "identical readback only, no second start or new expiry" }, + { "id": "legacy-id-collision", "expected": "new-profile admission denied without modifying historical receipt" }, + { "id": "unknown-profile-or-stripped-context", "expected": "no fallback, MISSING_AUTHORITY" }, + { "id": "unsupported-action-or-parameter", "expected": "SCOPE_CONFLICT, no observation" }, + { "id": "missing-or-corrupt-durable-readback", "expected": "denied before start intent/observation" }, + { "id": "absent-or-corrupt-status", "expected": "UNVERIFIED_ASSUMPTION, no observation" }, + { "id": "expiry-after-lock-wait", "expected": "STALE_PACKET, no observation" }, + { "id": "expiry-between-intent-and-handoff", "expected": "STALE_PACKET, retained intent, no observation or retry" }, + { "id": "revocation-before-handoff", "expected": "STALE_PACKET, zero observation, serialized status order evidenced" }, + { "id": "concurrent-revoke-start", "expected": "order visible; earlier revocation wins; no later work enabled" }, + { "id": "clock-rollback-or-unknown-time", "expected": "UNVERIFIED_ASSUMPTION, no observation" }, + { "id": "crash-after-intent-before-result", "expected": "reopen retains intent, HUMAN_DECISION_REQUIRED, no automatic observation" }, + { "id": "lost-response-or-ambiguous-observation", "expected": "durable uncertainty, HUMAN_DECISION_REQUIRED, no automatic retry" }, + { "id": "completed-start-replay", "expected": "historical readback only, no second observation" }, + { "id": "store-unavailable", "expected": "ENVIRONMENT_BLOCKED, no observation" }, + { "id": "revoked-status-reset", "expected": "rejected, terminal revocation and original slot preserved" } + ], + "boundary": "Inventory only; no fixture grants approval, runs a provider, launches work or proves release readiness." +} diff --git a/docs/source-packets/wits-v0/local-owner-profile.md b/docs/source-packets/wits-v0/local-owner-profile.md new file mode 100644 index 0000000..e9ebcb6 --- /dev/null +++ b/docs/source-packets/wits-v0/local-owner-profile.md @@ -0,0 +1,197 @@ +# Local Owner Continuation Profile Candidate + +Status: candidate contract for a bounded model-free implementation and proof. +Not a released profile, base conformance claim, or execution authority. + +## Identity And Scope + +- Candidate identity: `org.hacp.local-owner-continuation`, version `0.1-candidate`. +- Publisher: HACP maintainers; deployment issuer: explicitly configured owner. +- Base: `hacp-base-draft / v0.1-draft`, unchanged closed base records. +- Discovery: this document and its adjacent fixture inventory, pinned by Git + commit and SHA-256 before processing. Unknown identity/version fails closed. +- No active/revoked profile registry entry is published by this source packet. + A consuming implementation must explicitly select this candidate under owner + approval; a filename, hash, or successful validation is not that approval. + +The owner approved: an owner-controlled authenticated verifier, one consumption +slot per issuer plus decision ID, and fail-closed expiry/revocation checks +immediately before start. This contract specifies only a local, single-store, +model-free dry-run path. It is not a general policy engine or trust service. + +HACP references human authority; it never creates it. Consumption is not +execution completion; transport acceptance is not reading; evidence is not +approval. No hosted execution, model/tool dispatch, GitHub mutation, billing, +worker launch, release, deployment, autonomous ship or risk acceptance is added. + +## Supported Surface + +The implementation exposes one explicitly named authenticated local verifier +entry point for recording a human decision, admitting a claim, changing current +status, and attempting a model-free guarded start. It may be a local library +API exercised by tests; this packet does not authorize a new HTTP service. +The verifier authenticates the caller before any mutation or start check and +derives the issuer and human actor from owner-controlled configuration, never +from caller-supplied identity labels. A credential authenticates access, not a +human decision by itself: recording approval requires an explicit human act, +scope and evidence. Synthetic credentials/actors in tests must be labeled as +fixtures and cannot be used as actual approval evidence. + +An existing unauthenticated console, old consumption API, provider live-loop, +or receipt parser is NOT this entry point. Those paths remain legacy/demo-only +and cannot claim candidate support. No provider path is wired or run by this +packet. Never use a legacy path as fallback after verifier rejection. + +The local owner controls the process, datastore and authentication configuration; +compromise by that owner/host is outside this bounded trust model. Invalid, +missing or unrecognized authentication/configuration denies access before +reading protected artifacts or mutating state. No secret material belongs in +decisions, receipts, diagnostics or exported proof. Remote/multi-host identity, +key rotation and delegated approval are unsupported, not guessed defaults. + +## Decision And Claim Bindings + +The immutable approved decision binds issuer ID, decision ID, profile/version, +authenticated human actor/event reference, base decision reference and full +digest/domain, request reference, exact canonical action, approval time and +explicit expiry. Scope is exact equality for this candidate; subset inference, +natural-language equivalence and multi-action expansion are unsupported. + +The action is a JSON object containing a fixed model-free dry-run operation ID +and its declared parameters. Its grammar is closed by the implementation's +pinned adapter and declared in proof; unknown operation/parameter fails closed. +No operation may select a command, provider, network endpoint or code callback +from untrusted input. The effect is a bounded local test observation, not an +external side effect or a report claiming real work completed. + +All new profile records use full SHA-256 digests, domain separation, and RFC +8785 JCS over the complete record with its own top-level digest omitted. +Referenced record digests remain in that preimage. A digest declaration is +`algorithm: sha256`, `canonicalization: json-rfc8785-jcs`, a versioned domain, +and full hex value. Domain names are `..0.1-candidate`. +Required record kinds are decision, claim, status-event, start-intent, and +start-result. Unknown domain/version or any mismatched binding denies start. +Expiry is mandatory on decision and claim, finite RFC3339 UTC with millisecond +precision and a valid calendar date; null/absent, invalid and expired values +fail closed. Receipt expiry cannot exceed decision expiry. + +The datastore enforces uniqueness on `(issuerId, decisionId)` atomically. +Digest, request, action, profile version, attempt key and successor ID are +bindings to that slot, not extra dimensions that mint another slot. Changing +any bound content conflicts; changing successor conflicts. Exact claim retry +returns the original receipt without modifying it, extending expiry, starting +work or resetting status. Independently re-read the durable receipt and verify +all bindings; merely returning from INSERT is not sufficient start evidence. +The slot remains consumed after expiry, revocation, failure or uncertainty. + +## Current Status And Start Boundary + +The owner-controlled verifier is the sole issuer of current status in the same +authoritative local datastore as the slot. Status changes are authenticated, +append-only, digest-bound events for the exact decision/claim. Revocation is +terminal in this candidate: no un-revoke or status reset. Never infer active +status from an absent row, a caller snapshot, a receipt's old accepted flag, +or a self-supplied URI. Both decision and claim must have known active status. + +A guarded start requires a fresh authenticated request to this verifier, exact +decision/claim/successor/action bindings, durable receipt readback and an unused +start slot. In one serialized boundary shared with status mutations: + +1. Obtain the write/serialization guard, then read current status and time. + Check expiry AFTER any wait; `now >= expiry` denies start. +2. Verify complete status integrity/order and active state for both records. + Unknown/missing/tampered status or untrustworthy time denies start. +3. Durably record the one-shot start intent before any invocation attempt. + Commit of that intent is **start admission**, not evidence work began. +4. Only that uninterrupted call may attempt the fixed local dry-run observation + at most once. Recheck time immediately before that observation; if expired, + stop without work. Revocation checks and this handoff must share the same + serialization guard so a prior committed revocation cannot be overlooked. +5. Record a separate result describing observed completion or uncertainty. + A missing result never implies success and never permits a second attempt. + +The implementation must document how its guard spans durable intent and the +local handoff; database atomicity alone does not make external execution atomic. +If the implementation cannot enforce this local interval, it must deny start, +not rename an earlier snapshot as an immediate-before-start check. Revocation +ordered before the handoff wins; later revocation cannot undo an observation +already made, and cannot authorize further work. The status/clock assumptions +and order must be inspectable in proof. Clock rollback/unknown freshness blocks; +no caller-controlled clock override exists outside explicit test injection. + +Process interruption, lost response, existing start intent, unknown outcome, +restarted process or any recovery request routes to human inspection. No +automatic recovery, stale-lock reclaim, retry, receipt reset, new successor, +or exactly-once external-effect promise. Store reopen for historical readback +is supported; resume/reexecution is not. A new human decision is needed for +any separately authorized later work, with the original history preserved. + +## Diagnostics And Base Compatibility + +These are candidate diagnostics, not new base decision/authority enums: + +| Condition | Base stop mapping | +| --- | --- | +| Unauthenticated request, missing decision/claim/profile | MISSING_AUTHORITY | +| Changed scope or unsupported operation | SCOPE_CONFLICT | +| Expired or revoked decision/claim | STALE_PACKET | +| Corrupt bindings/status, unknown order or clock | UNVERIFIED_ASSUMPTION | +| Store/serialization unavailable | ENVIRONMENT_BLOCKED | +| Existing start intent, recovery or ambiguous result | HUMAN_DECISION_REQUIRED | + +If multiple conditions apply, authentication is checked first; known +expiry/revocation may deny before other integrity details are disclosed. No +failure may dispatch work. A base stop must still carry all fields required by +its own schema. Auth failures may return a minimal access denial without +disclosing protected record existence. Candidate receipts extend no closed +base schema: use separate required-context records bound to the base decision. +Missing extension context cannot fall back to base-only authority. + +RFC-0006 single-pass posture applies: zero additional loop cycles and no +automatic claim/start retry. Exact claim readback is not another execution +cycle. Runtime-identity attestation is preflight evidence under RFC-0008/0009, +not approval. Allowance/reservation evidence is distinct from loop counters +and verified spend; unused capacity creates no authority. Unknown outcomes +remain explicit, never inferred completion. + +## Migration And Unsupported Capabilities + +who-decides main `99a256dd870b723b38d1b6b287dd4279f8a72fdf` has proven local +consumption integrity under its own `who-decides.decision.v1` encoding. That +encoding is not this JCS/domain profile. Preserve it and its historical proof; +do not silently reinterpret old hashes or mutate existing receipts. + +Old receipts without issuer, authenticated approval, complete expiry or current +status are read-only history. They cannot authorize this profile's start. A +new issuer cannot bypass old consumption by reusing an old decision ID: until +an explicit migration is approved, any matching legacy decision ID blocks new +profile admission. Do not guess its issuer, delete its row or reset its slot. +New decisions use fresh IDs and explicit approval; no automated migration or +grant renewal is part of this packet. Changing profile version does not free a +slot. The owner must separately approve future namespace/key migration. + +| Capability | Candidate contract | Evidence status at initial pin | +| --- | --- | --- | +| Immutable consumption, conflict detection, expiry under write lock | Preserve existing behavior | Existing who-decides tests/proof; not yet this profile | +| Authenticated issuer+decision slot and exact action | Required | Pending new implementation/proof | +| Current status and guarded start, expiry/revocation race | Required | Pending new implementation/proof | +| Historical readback after restart | Supported | Receipt proof exists; new profile proof pending | +| Recovery/reexecution after interruption | Explicit human-inspection stop | Negative proof required | +| Unauthenticated console or real provider path | Unsupported | No candidate authority/conformance claim | +| Distributed trust, remote execution, billing or release | Unsupported | No implementation or proof claim | + +## Proof Gate + +The adjacent fixture inventory defines required positive/negative observations. +Capture pinned source/profile/harness hashes, authenticated fixture provenance, +actual process overlap/serialization observations, durable receipt/status/start +records and exact outcomes. Fixtures contain no real credentials; synthetic +owner acts are not Joe's proof acceptance. No test-name, schema pass, review, +transport receipt or merge substitutes for observed behavior or human approval. + +Before claiming this candidate implemented, verify actual who-decides evidence +against this same pinned contract. Before material adoption, require two +independent reviewers with identical pins, no priming between them, and an +evidence-based synthesis. Available Codex reviewers can perform the bounded +reviews; no additional paid Kiro permission exists. Release/deployment/merge +are not authorized. Proof acceptance and any release decision remain human. From 21a59ddef73d5b626baa49f37871f0392276e5fc Mon Sep 17 00:00:00 2001 From: Joe Feser Date: Fri, 4 Sep 2026 13:25:30 -0500 Subject: [PATCH 2/6] Resolve local continuation review gaps --- docs/source-packets/README.md | 3 + .../accountable-continuation-v0.3-draft.md | 6 + .../wits-v0/local-owner-profile-fixtures.json | 19 +++- .../local-owner-profile-review-synthesis.md | 63 +++++++++++ .../wits-v0/local-owner-profile.md | 106 +++++++++++++++--- 5 files changed, 181 insertions(+), 16 deletions(-) create mode 100644 docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md diff --git a/docs/source-packets/README.md b/docs/source-packets/README.md index 6b253fb..3e049f3 100644 --- a/docs/source-packets/README.md +++ b/docs/source-packets/README.md @@ -14,6 +14,9 @@ the normal RFC, schema, profile, and fixture process. - [WITS v0 accountable continuation source packet](wits-v0/accountable-continuation-v0.3-draft.md) for HACP v0.3 candidate consumption receipts, successor invocation evidence, and fail-closed continuation boundaries. +- [Local owner continuation profile candidate](wits-v0/local-owner-profile.md) + narrows the approved local verifier/issuer/start policy; implementation proof + and explicit acceptance remain separate gates. ## Boundaries diff --git a/docs/source-packets/wits-v0/accountable-continuation-v0.3-draft.md b/docs/source-packets/wits-v0/accountable-continuation-v0.3-draft.md index 7e384e8..99e876a 100644 --- a/docs/source-packets/wits-v0/accountable-continuation-v0.3-draft.md +++ b/docs/source-packets/wits-v0/accountable-continuation-v0.3-draft.md @@ -2,6 +2,12 @@ Status: HACP v0.3 candidate source packet. +The [local owner profile candidate](local-owner-profile.md) is a separately +pinned, narrow application of the owner's verifier/issuer/start policy. It does +not resolve policy for every possible continuation profile or establish +implementation proof. The illustrative sketches below remain historical +candidate shapes, not interchangeable records for that local profile. + Source material: - [WITS v0 HACP source packet](../wits-v0-hacp-source-packet.md); diff --git a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json index 4a6ac19..4fd8af7 100644 --- a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json +++ b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json @@ -6,26 +6,43 @@ "cases": [ { "id": "authenticated-happy-path", "expected": "one durable claim and start intent, one dry-run observation, separate result" }, { "id": "missing-or-wrong-authentication", "expected": "access denied before protected read, mutation or observation" }, + { "id": "authenticated-without-human-act", "expected": "MISSING_AUTHORITY, authentication alone records no decision and enables no observation" }, + { "id": "unrelated-or-reused-human-act", "expected": "MISSING_AUTHORITY, event must bind this decision, packet and exact action" }, + { "id": "base-approve-without-start-work", "expected": "MISSING_AUTHORITY, approve_next_packet does not authorize candidate observation" }, + { "id": "invalid-base-transition-or-target", "expected": "rejected by closed base schema and matrix checks, no candidate mutation" }, { "id": "caller-forged-issuer-or-actor", "expected": "rejected, configured identity cannot be overridden" }, { "id": "two-overlapping-claims", "expected": "one issuer/decision slot, one winner, competing successor denied" }, { "id": "same-id-different-issuer", "expected": "separate configured issuer slots; no cross-issuer access or caller reassignment" }, { "id": "changed-binding-replay", "expected": "conflict for changed digest/request/action/expiry/profile/successor on both replay paths" }, { "id": "exact-claim-retry", "expected": "identical readback only, no second start or new expiry" }, { "id": "legacy-id-collision", "expected": "new-profile admission denied without modifying historical receipt" }, + { "id": "legacy-insert-races-profile-admission", "expected": "one serialized namespace outcome; collision denies candidate and no duplicate decision ID exists" }, { "id": "unknown-profile-or-stripped-context", "expected": "no fallback, MISSING_AUTHORITY" }, { "id": "unsupported-action-or-parameter", "expected": "SCOPE_CONFLICT, no observation" }, { "id": "missing-or-corrupt-durable-readback", "expected": "denied before start intent/observation" }, { "id": "absent-or-corrupt-status", "expected": "UNVERIFIED_ASSUMPTION, no observation" }, + { "id": "status-gap-fork-truncation-or-wrong-head", "expected": "UNVERIFIED_ASSUMPTION for each history defect, no observation" }, + { "id": "decision-expired-claim-active", "expected": "STALE_PACKET, no observation" }, + { "id": "claim-expired-decision-active", "expected": "STALE_PACKET, no observation" }, + { "id": "decision-revoked-claim-active", "expected": "STALE_PACKET, no observation" }, + { "id": "claim-revoked-decision-active", "expected": "STALE_PACKET, no observation" }, + { "id": "missing-or-malformed-expiry", "expected": "decision and claim variants rejected before admission" }, + { "id": "claim-expiry-exceeds-decision", "expected": "rejected before claim admission" }, { "id": "expiry-after-lock-wait", "expected": "STALE_PACKET, no observation" }, { "id": "expiry-between-intent-and-handoff", "expected": "STALE_PACKET, retained intent, no observation or retry" }, { "id": "revocation-before-handoff", "expected": "STALE_PACKET, zero observation, serialized status order evidenced" }, { "id": "concurrent-revoke-start", "expected": "order visible; earlier revocation wins; no later work enabled" }, + { "id": "two-overlapping-starts", "expected": "one durable intent and at most one dry-run observation; loser stops with retained state" }, { "id": "clock-rollback-or-unknown-time", "expected": "UNVERIFIED_ASSUMPTION, no observation" }, + { "id": "restart-after-claim-before-intent", "expected": "historical readback only; a new start requires a fresh authenticated call and all current checks" }, { "id": "crash-after-intent-before-result", "expected": "reopen retains intent, HUMAN_DECISION_REQUIRED, no automatic observation" }, { "id": "lost-response-or-ambiguous-observation", "expected": "durable uncertainty, HUMAN_DECISION_REQUIRED, no automatic retry" }, { "id": "completed-start-replay", "expected": "historical readback only, no second observation" }, { "id": "store-unavailable", "expected": "ENVIRONMENT_BLOCKED, no observation" }, - { "id": "revoked-status-reset", "expected": "rejected, terminal revocation and original slot preserved" } + { "id": "revoked-status-reset", "expected": "rejected, terminal revocation and original slot preserved" }, + { "id": "digest-known-answer-and-domain-mismatch", "expected": "published vector matches; omitted/changed domain and alternate preimage fail" }, + { "id": "closed-base-record-remains-unchanged", "expected": "candidate context is separate and base schema still validates without extension fields" }, + { "id": "unsupported-surface-no-fallback", "expected": "legacy console, provider loop, HTTP service and callback dispatch cannot invoke candidate start" } ], "boundary": "Inventory only; no fixture grants approval, runs a provider, launches work or proves release readiness." } diff --git a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md new file mode 100644 index 0000000..64b6165 --- /dev/null +++ b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md @@ -0,0 +1,63 @@ +# Local Owner Continuation Review Synthesis + +Status: bounded candidate review evidence, not release approval. + +## Reviewed Pin + +- Candidate commit: `f4df7be755569f1c5f4c2ab6925fe0be62cad066` +- Profile SHA-256: `1c358c5d058fade2723cd02e1dbd3bbb7f9134cfb7f3e3988c35051eaaf001b1` +- Fixture inventory SHA-256: `4b954d85cac1ccd2ee9f592672599cac5104a3cac9e3e0aa80da79ebf06a02bb` +- HACP base: `3ada8c8e86dc181df4f954204eaba2b5426f47f2` +- who-decides reference: `99a256dd870b723b38d1b6b287dd4279f8a72fdf` + +Two fresh Codex review contexts independently read those exact pins. Neither +received the other review, edited files, called providers, or mutated GitHub. + +## Synthesis + +Both reviewers found no P1. Both independently found P2 gaps in: + +- verification that a human act and closed-base decision authorize this exact + packet and action; +- deterministic domain-separated digest preimages and record/status contracts; +- explicit negative and concurrency observations for authority and one-shot + behavior. + +One reviewer separately classified the clock acceptance rule as a P2. The other +called clock trust an unresolved implementation obligation rather than a +separate finding. This is a classification difference, not evidence that the +old rule was executable. + +Both agreed the bounded architecture is coherent, the guard must span durable +intent through immediate local handoff, issuer plus decision ID is the slot, +legacy collisions must fail closed, and no new owner decision is needed for the +minimal repairs. Neither review established implementation, runtime proof, +external exactly-once effects, profile conformance, release readiness, or human +acceptance. + +## Disposition + +The follow-up candidate: + +- requires a valid base `start_work` human decision for the same packet and + exact action, and treats the dry-run as observation rather than a lifecycle + transition; +- fixes the UTF-8 JCS digest envelope and known-answer vector; +- closes the fixed action and minimal record contracts; +- defines initial status, predecessor ordering and authoritative heads; +- defines local wall/monotonic clock checks with no grace period; +- adds distinct authority, expiry, revocation, status-integrity, restart, + start-race, legacy-race, digest and unsupported-surface observations. + +These dispositions preserve the approved policy. Delegation, remote trust, +external dispatch, automatic recovery/reexecution, migration and relaxed clock +acceptance remain unsupported and would require separate owner decisions. + +## Residual Risk + +The repaired text and inventory are specifications only. Candidate support +still requires a same-pin implementation, executable schemas/fixtures, observed +serialization and crash evidence, independent proof readback, and human proof +acceptance. Two reviews from the same tool family are independent architecture +reviews for this bounded pass; they are not a cross-tool ship gate or release +authority. diff --git a/docs/source-packets/wits-v0/local-owner-profile.md b/docs/source-packets/wits-v0/local-owner-profile.md index e9ebcb6..f7363f1 100644 --- a/docs/source-packets/wits-v0/local-owner-profile.md +++ b/docs/source-packets/wits-v0/local-owner-profile.md @@ -57,20 +57,74 @@ digest/domain, request reference, exact canonical action, approval time and explicit expiry. Scope is exact equality for this candidate; subset inference, natural-language equivalence and multi-action expansion are unsupported. -The action is a JSON object containing a fixed model-free dry-run operation ID -and its declared parameters. Its grammar is closed by the implementation's -pinned adapter and declared in proof; unknown operation/parameter fails closed. -No operation may select a command, provider, network endpoint or code callback -from untrusted input. The effect is a bounded local test observation, not an -external side effect or a report claiming real work completed. +The referenced base Human Decision Gate MUST pass the closed v0.1 schema and +published decision-matrix checks. For this candidate it MUST be a distinct +human `start_work` act for the same packet, validated as `approved` to +`in_progress`, with `actor_kind: human`, a trusted actor verification source, +and evidence that identifies this exact candidate decision and canonical +action. The verifier resolves that evidence through its owner-controlled local +store; a caller-supplied reference, authenticated service request, unrelated +`approve_next_packet`, automated event, cancellation, closeout or reused human +act is not applicable authority. The dry-run observation does not create or +change base lifecycle status. It only tests whether an already-valid base +`start_work` decision and this extension remain admissible. + +The only action is the exact JSON value +`{"operationId":"observe_fixed_payload","parameters":{"payload":"HACP_LOCAL_OWNER_CONTINUATION_PROBE_V1"}}`. +No additional operation, parameter or value is allowed. The implementation may +only compare and synchronously return that fixed payload; it may not interpret +it as a command, provider, network endpoint or code callback. The effect is a +bounded local test observation, not an external side effect or a report +claiming real work completed. All new profile records use full SHA-256 digests, domain separation, and RFC -8785 JCS over the complete record with its own top-level digest omitted. -Referenced record digests remain in that preimage. A digest declaration is -`algorithm: sha256`, `canonicalization: json-rfc8785-jcs`, a versioned domain, -and full hex value. Domain names are `..0.1-candidate`. -Required record kinds are decision, claim, status-event, start-intent, and -start-result. Unknown domain/version or any mismatched binding denies start. +8785 JCS. The exact hash input is the UTF-8 encoding, with no BOM or trailing +newline, of this envelope: + +```json +{"domain":"..0.1-candidate","record":} +``` + +The `domain` member is therefore inside the hashed preimage. Referenced record +digests remain inside `record`. A digest declaration, stored outside the hash +input it describes, is `algorithm: sha256`, `canonicalization: +json-rfc8785-jcs`, the same versioned `domain`, and a 64-character lowercase +hex `value`. Required record kinds are `decision`, `claim`, `status-event`, +`start-intent`, and `start-result`; every record contains `recordKind`, +`profileId`, and `profileVersion`. Unknown domain/version or any mismatched +binding denies start. + +This small known-answer vector fixes envelope interpretation; it is a digest +algorithm check, not a complete decision fixture: + +```json +{"domain":"org.hacp.local-owner-continuation.decision.0.1-candidate","record":{"decisionId":"decision-example-001","issuerId":"issuer-example","profileId":"org.hacp.local-owner-continuation","profileVersion":"0.1-candidate","recordKind":"decision"}} +``` + +Its SHA-256 is +`9de745ae777609863f309450a0455da5ad7a1d166f8f29734d8a2d35d569f014`. +The base decision reference retains the base record's own declared digest and +canonicalization; the candidate stores that complete lowercase hex value and +does not reinterpret or rewrite the closed base record. + +The minimal candidate record contracts are closed: unknown members fail. Common +members on all five are `recordKind`, `profileId`, `profileVersion`, `issuerId`, +`decisionId`, and `digest`. In addition: + +| Kind | Additional required members | +| --- | --- | +| `decision` | `humanEventRef`, `baseDecisionRef`, `baseDecisionDigest`, `requestRef`, `action`, `approvedAt`, `expiresAt` | +| `claim` | `decisionDigest`, `claimId`, `attemptKey`, `successorId`, `requestRef`, `action`, `claimedAt`, `expiresAt` | +| `status-event` | `eventId`, `targetKind`, `targetDigest`, `sequence`, `previousDigest`, `state`, `recordedAt`, `actorId` | +| `start-intent` | `claimDigest`, `intentId`, `successorId`, `action`, `admittedAt`, `decisionStatusHead`, `claimStatusHead`, `clockSample` | +| `start-result` | `intentDigest`, `resultId`, `outcome`, `observedAt`, `observationDigest` | + +`outcome` is exactly `completed` or `uncertain`; absence is not an outcome. +`clockSample` contains the accepted UTC wall timestamp and monotonic reading, +never a caller timestamp. IDs and references are non-empty strings; timestamps +use the expiry format below; sequence is a non-negative integer; digest members +use the declaration above. The implementation proof MUST publish executable +schemas matching this table before claiming candidate support. Expiry is mandatory on decision and claim, finite RFC3339 UTC with millisecond precision and a valid calendar date; null/absent, invalid and expired values fail closed. Receipt expiry cannot exceed decision expiry. @@ -93,6 +147,17 @@ terminal in this candidate: no un-revoke or status reset. Never infer active status from an absent row, a caller snapshot, a receipt's old accepted flag, or a self-supplied URI. Both decision and claim must have known active status. +Each decision and claim is created with an initial `active` status event in the +same serialized mutation that records it. A status event contains `eventId`, +`targetKind` (`decision` or `claim`), `targetDigest`, strictly increasing +`sequence`, `previousDigest` (null only at sequence 0), `state` (`active` or +`revoked`), `recordedAt`, configured issuer/actor bindings, and its own digest. +The datastore retains an authoritative head digest for each target and changes +that head atomically with the append. Verification walks sequence 0 through the +head, checks every predecessor/digest/target binding, rejects gaps, forks, +truncation, multiple heads or a head not matching the stored authoritative +head, and derives current state only from the verified final event. + A guarded start requires a fresh authenticated request to this verifier, exact decision/claim/successor/action bindings, durable receipt readback and an unused start slot. In one serialized boundary shared with status mutations: @@ -115,9 +180,18 @@ local handoff; database atomicity alone does not make external execution atomic. If the implementation cannot enforce this local interval, it must deny start, not rename an earlier snapshot as an immediate-before-start check. Revocation ordered before the handoff wins; later revocation cannot undo an observation -already made, and cannot authorize further work. The status/clock assumptions -and order must be inspectable in proof. Clock rollback/unknown freshness blocks; -no caller-controlled clock override exists outside explicit test injection. +already made, and cannot authorize further work. The accepted clock is an +owner-configured local UTC wall clock paired with a monotonic process clock. +The verifier stores the latest accepted wall-clock sample in the authoritative +store. Within the uninterrupted guarded call it samples both clocks after +acquiring the guard and again immediately before observation; wall and +monotonic samples MUST NOT move backward, and the wall sample MUST NOT precede +the durable prior wall sample. Every expiry comparison uses the later wall +sample and has no grace period. Unavailable clocks, invalid samples, rollback, +inability to read/update the durable sample, or inability to keep the same +guarded process interval makes freshness unknown and blocks. No caller-controlled +clock override exists outside explicit test injection, and restart never +resumes an existing intent. Process interruption, lost response, existing start intent, unknown outcome, restarted process or any recovery request routes to human inspection. No @@ -195,3 +269,5 @@ independent reviewers with identical pins, no priming between them, and an evidence-based synthesis. Available Codex reviewers can perform the bounded reviews; no additional paid Kiro permission exists. Release/deployment/merge are not authorized. Proof acceptance and any release decision remain human. +The initial independent findings and their bounded repair disposition are in +[the review synthesis](local-owner-profile-review-synthesis.md). From 20c0a499cafe2c5d0939f8ee34bb18cca0dea63c Mon Sep 17 00:00:00 2001 From: Joe Feser Date: Fri, 4 Sep 2026 13:29:08 -0500 Subject: [PATCH 3/6] Define detached base decision digest --- .../wits-v0/local-owner-profile-fixtures.json | 1 + .../local-owner-profile-review-synthesis.md | 2 ++ .../wits-v0/local-owner-profile.md | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json index 4fd8af7..384af28 100644 --- a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json +++ b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json @@ -41,6 +41,7 @@ { "id": "store-unavailable", "expected": "ENVIRONMENT_BLOCKED, no observation" }, { "id": "revoked-status-reset", "expected": "rejected, terminal revocation and original slot preserved" }, { "id": "digest-known-answer-and-domain-mismatch", "expected": "published vector matches; omitted/changed domain and alternate preimage fail" }, + { "id": "base-decision-companion-digest", "expected": "detached candidate domain binds the complete unchanged base record; mismatch fails without claiming a native base digest" }, { "id": "closed-base-record-remains-unchanged", "expected": "candidate context is separate and base schema still validates without extension fields" }, { "id": "unsupported-surface-no-fallback", "expected": "legacy console, provider loop, HTTP service and callback dispatch cannot invoke candidate start" } ], diff --git a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md index 64b6165..b13b3c3 100644 --- a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md +++ b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md @@ -43,6 +43,8 @@ The follow-up candidate: exact action, and treats the dry-run as observation rather than a lifecycle transition; - fixes the UTF-8 JCS digest envelope and known-answer vector; +- defines a candidate-owned detached digest for the unchanged closed base + decision, which has no native digest declaration; - closes the fixed action and minimal record contracts; - defines initial status, predecessor ordering and authoritative heads; - defines local wall/monotonic clock checks with no grace period; diff --git a/docs/source-packets/wits-v0/local-owner-profile.md b/docs/source-packets/wits-v0/local-owner-profile.md index f7363f1..906c3b0 100644 --- a/docs/source-packets/wits-v0/local-owner-profile.md +++ b/docs/source-packets/wits-v0/local-owner-profile.md @@ -103,9 +103,21 @@ algorithm check, not a complete decision fixture: Its SHA-256 is `9de745ae777609863f309450a0455da5ad7a1d166f8f29734d8a2d35d569f014`. -The base decision reference retains the base record's own declared digest and -canonicalization; the candidate stores that complete lowercase hex value and -does not reinterpret or rewrite the closed base record. +The closed v0.1 Human Decision Gate does not declare a native digest. This +candidate therefore binds it through a detached companion digest declaration +inside the candidate decision; it does not add a field to or claim a digest for +the base record itself. The companion hash input is UTF-8 RFC 8785 JCS of: + +```json +{"domain":"org.hacp.local-owner-continuation.base-decision-reference.0.1-candidate","record":} +``` + +`baseDecisionDigest` is the corresponding digest declaration with `algorithm: +sha256`, `canonicalization: json-rfc8785-jcs`, that exact `domain`, and the full +lowercase hex `value`. The verifier first validates the unchanged base record +against its closed schema and matrix, then verifies this detached candidate +binding. An implementation must not reinterpret this companion digest as a +native base-record digest or mutate the base record to carry it. The minimal candidate record contracts are closed: unknown members fail. Common members on all five are `recordKind`, `profileId`, `profileVersion`, `issuerId`, From de8a2a7a0104d4f1a67f866d20d32ebf30ee8752 Mon Sep 17 00:00:00 2001 From: Joe Feser Date: Sat, 5 Sep 2026 09:20:31 -0500 Subject: [PATCH 4/6] Resolve hosted continuation profile findings --- .../wits-v0/local-owner-profile-fixtures.json | 5 +- .../local-owner-profile-review-synthesis.md | 6 +++ .../wits-v0/local-owner-profile.md | 52 +++++++++++++++---- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json index 384af28..363775c 100644 --- a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json +++ b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json @@ -34,13 +34,16 @@ { "id": "concurrent-revoke-start", "expected": "order visible; earlier revocation wins; no later work enabled" }, { "id": "two-overlapping-starts", "expected": "one durable intent and at most one dry-run observation; loser stops with retained state" }, { "id": "clock-rollback-or-unknown-time", "expected": "UNVERIFIED_ASSUMPTION, no observation" }, - { "id": "restart-after-claim-before-intent", "expected": "historical readback only; a new start requires a fresh authenticated call and all current checks" }, + { "id": "restart-after-claim-before-intent", "expected": "historical readback only, HUMAN_DECISION_REQUIRED, no start or automatic continuation" }, { "id": "crash-after-intent-before-result", "expected": "reopen retains intent, HUMAN_DECISION_REQUIRED, no automatic observation" }, { "id": "lost-response-or-ambiguous-observation", "expected": "durable uncertainty, HUMAN_DECISION_REQUIRED, no automatic retry" }, { "id": "completed-start-replay", "expected": "historical readback only, no second observation" }, { "id": "store-unavailable", "expected": "ENVIRONMENT_BLOCKED, no observation" }, { "id": "revoked-status-reset", "expected": "rejected, terminal revocation and original slot preserved" }, { "id": "digest-known-answer-and-domain-mismatch", "expected": "published vector matches; omitted/changed domain and alternate preimage fail" }, + { "id": "profile-declaration-status", "expected": "active exact-pin candidate may be explicitly selected; missing, changed, deprecated or revoked declaration is rejected" }, + { "id": "clock-sample-shape", "expected": "only wallTime plus canonical decimal-string monotonicNanoseconds accepted; extra members and alternate numeric forms fail" }, + { "id": "observation-digest-completed-or-uncertain", "expected": "completed binds the exact fixed observation envelope; uncertain requires null and makes no observation claim" }, { "id": "base-decision-companion-digest", "expected": "detached candidate domain binds the complete unchanged base record; mismatch fails without claiming a native base digest" }, { "id": "closed-base-record-remains-unchanged", "expected": "candidate context is separate and base schema still validates without extension fields" }, { "id": "unsupported-surface-no-fallback", "expected": "legacy console, provider loop, HTTP service and callback dispatch cannot invoke candidate start" } diff --git a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md index b13b3c3..61eb920 100644 --- a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md +++ b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md @@ -51,6 +51,12 @@ The follow-up candidate: - adds distinct authority, expiry, revocation, status-integrity, restart, start-race, legacy-race, digest and unsupported-surface observations. +Hosted review on PR #43 additionally required the RFC-0009 `active` declaration, +closed clock-sample representation, deterministic completed/uncertain +observation digest, and one restart rule. Those repairs retain explicit owner +selection, use canonical decimal-string monotonic nanoseconds, assert no digest +for an uncertain observation, and route every restart to human inspection. + These dispositions preserve the approved policy. Delegation, remote trust, external dispatch, automatic recovery/reexecution, migration and relaxed clock acceptance remain unsupported and would require separate owner decisions. diff --git a/docs/source-packets/wits-v0/local-owner-profile.md b/docs/source-packets/wits-v0/local-owner-profile.md index 906c3b0..67d3d48 100644 --- a/docs/source-packets/wits-v0/local-owner-profile.md +++ b/docs/source-packets/wits-v0/local-owner-profile.md @@ -6,13 +6,26 @@ Not a released profile, base conformance claim, or execution authority. ## Identity And Scope - Candidate identity: `org.hacp.local-owner-continuation`, version `0.1-candidate`. +- Profile status: `active`, only for explicit owner-approved candidate + processing under this declaration. This status does not mean released, + conformant, implemented, deployed or generally enabled. - Publisher: HACP maintainers; deployment issuer: explicitly configured owner. - Base: `hacp-base-draft / v0.1-draft`, unchanged closed base records. - Discovery: this document and its adjacent fixture inventory, pinned by Git commit and SHA-256 before processing. Unknown identity/version fails closed. -- No active/revoked profile registry entry is published by this source packet. - A consuming implementation must explicitly select this candidate under owner - approval; a filename, hash, or successful validation is not that approval. +- Discovery is by bundled artifact plus the integrity pin above; no registry + entry or stable release URL is published. A consuming implementation must + explicitly select this active candidate under owner approval; a filename, + hash, status or successful validation is not that approval. + +This section is the RFC-0009 profile declaration. It adds the five record kinds +and fields defined below, but no base authority or decision vocabulary. Its +authority impact is limited to testing an already-valid human `start_work` +decision through the fixed local observation. It removes optional recovery and +all action choice, confirms the forbidden effects in this document, adds the +43 observations in the adjacent inventory, and uses the compatibility and +migration rules below. Consumers MUST read this declaration before processing +candidate records and reject a missing, changed, deprecated or revoked pin. The owner approved: an owner-controlled authenticated verifier, one consumption slot per issuer plus decision ID, and fail-closed expiry/revocation checks @@ -129,14 +142,32 @@ members on all five are `recordKind`, `profileId`, `profileVersion`, `issuerId`, | `claim` | `decisionDigest`, `claimId`, `attemptKey`, `successorId`, `requestRef`, `action`, `claimedAt`, `expiresAt` | | `status-event` | `eventId`, `targetKind`, `targetDigest`, `sequence`, `previousDigest`, `state`, `recordedAt`, `actorId` | | `start-intent` | `claimDigest`, `intentId`, `successorId`, `action`, `admittedAt`, `decisionStatusHead`, `claimStatusHead`, `clockSample` | -| `start-result` | `intentDigest`, `resultId`, `outcome`, `observedAt`, `observationDigest` | +| `start-result` | `intentDigest`, `resultId`, `outcome`, `observedAt`, `observationClockSample`, `observationDigest` | `outcome` is exactly `completed` or `uncertain`; absence is not an outcome. -`clockSample` contains the accepted UTC wall timestamp and monotonic reading, -never a caller timestamp. IDs and references are non-empty strings; timestamps -use the expiry format below; sequence is a non-negative integer; digest members -use the declaration above. The implementation proof MUST publish executable -schemas matching this table before claiming candidate support. +`clockSample` and `observationClockSample` are closed JSON objects with exactly +`wallTime` and `monotonicNanoseconds`. `wallTime` uses the timestamp format below. +`monotonicNanoseconds` is a non-negative base-10 integer encoded as the JSON +string `"0"` or a string matching `[1-9][0-9]*`, with no sign, leading zero, +fraction or exponent; implementations compare it as arbitrary-precision +nanoseconds from one process-local monotonic clock. Neither sample is supplied +by the caller. `admittedAt` equals `clockSample.wallTime`; `observedAt` equals +`observationClockSample.wallTime`. IDs and references are non-empty strings; +sequence is a non-negative integer; digest members use the declaration above. The +implementation proof MUST publish executable schemas matching this table +before claiming candidate support. + +For `completed`, `observationDigest` is a digest declaration for UTF-8 RFC 8785 +JCS of this exact observation envelope: + +```json +{"domain":"org.hacp.local-owner-continuation.observation.0.1-candidate","record":{"operationId":"observe_fixed_payload","payload":"HACP_LOCAL_OWNER_CONTINUATION_PROBE_V1"}} +``` + +Its SHA-256 is +`2291610e38245f88bac99efc480897600b1f322a4004d0113487033a3b13de5e`. +For `uncertain`, `observationDigest` is JSON null: no observation is asserted +or hashed. Any other observation member, domain, payload or digest shape fails. Expiry is mandatory on decision and claim, finite RFC3339 UTC with millisecond precision and a valid calendar date; null/absent, invalid and expired values fail closed. Receipt expiry cannot exceed decision expiry. @@ -206,7 +237,8 @@ clock override exists outside explicit test injection, and restart never resumes an existing intent. Process interruption, lost response, existing start intent, unknown outcome, -restarted process or any recovery request routes to human inspection. No +restarted process (including restart after claim but before intent) or any +recovery request routes to human inspection. No automatic recovery, stale-lock reclaim, retry, receipt reset, new successor, or exactly-once external-effect promise. Store reopen for historical readback is supported; resume/reexecution is not. A new human decision is needed for From 664dc73e14d5bd3647e504604b1630e090509100 Mon Sep 17 00:00:00 2001 From: Joe Feser Date: Sat, 5 Sep 2026 09:26:30 -0500 Subject: [PATCH 5/6] Clarify guarded expiry sample ordering --- .../wits-v0/local-owner-profile-review-synthesis.md | 3 +++ docs/source-packets/wits-v0/local-owner-profile.md | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md index 61eb920..50da5ad 100644 --- a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md +++ b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md @@ -56,6 +56,9 @@ closed clock-sample representation, deterministic completed/uncertain observation digest, and one restart rule. Those repairs retain explicit owner selection, use canonical decimal-string monotonic nanoseconds, assert no digest for an uncertain observation, and route every restart to human inspection. +Fresh-head Codex review then identified a contradictory clock sentence; the +final repair assigns the acquisition sample to the post-lock check and the +second sample to the immediate pre-observation recheck. These dispositions preserve the approved policy. Delegation, remote trust, external dispatch, automatic recovery/reexecution, migration and relaxed clock diff --git a/docs/source-packets/wits-v0/local-owner-profile.md b/docs/source-packets/wits-v0/local-owner-profile.md index 67d3d48..4dfadc4 100644 --- a/docs/source-packets/wits-v0/local-owner-profile.md +++ b/docs/source-packets/wits-v0/local-owner-profile.md @@ -229,8 +229,10 @@ The verifier stores the latest accepted wall-clock sample in the authoritative store. Within the uninterrupted guarded call it samples both clocks after acquiring the guard and again immediately before observation; wall and monotonic samples MUST NOT move backward, and the wall sample MUST NOT precede -the durable prior wall sample. Every expiry comparison uses the later wall -sample and has no grace period. Unavailable clocks, invalid samples, rollback, +the durable prior wall sample. The initial post-lock expiry check uses the +acquisition `clockSample`; the immediate pre-observation expiry recheck uses the +later `observationClockSample`. Both use `now >= expiry` with no grace period. +Unavailable clocks, invalid samples, rollback, inability to read/update the durable sample, or inability to keep the same guarded process interval makes freshness unknown and blocks. No caller-controlled clock override exists outside explicit test injection, and restart never From 3b61e64d61984f0c5617c4a71266802f31961494 Mon Sep 17 00:00:00 2001 From: Joe Feser Date: Sat, 5 Sep 2026 09:43:51 -0500 Subject: [PATCH 6/6] Bind expiry to monotonic deadline --- .../wits-v0/local-owner-profile-fixtures.json | 1 + .../local-owner-profile-review-synthesis.md | 4 ++++ .../wits-v0/local-owner-profile.md | 19 +++++++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json index 363775c..502e3fb 100644 --- a/docs/source-packets/wits-v0/local-owner-profile-fixtures.json +++ b/docs/source-packets/wits-v0/local-owner-profile-fixtures.json @@ -34,6 +34,7 @@ { "id": "concurrent-revoke-start", "expected": "order visible; earlier revocation wins; no later work enabled" }, { "id": "two-overlapping-starts", "expected": "one durable intent and at most one dry-run observation; loser stops with retained state" }, { "id": "clock-rollback-or-unknown-time", "expected": "UNVERIFIED_ASSUMPTION, no observation" }, + { "id": "wall-clock-stall-crosses-monotonic-deadline", "expected": "STALE_PACKET when monotonic deadline is reached even if wallTime remains before expiry, no observation" }, { "id": "restart-after-claim-before-intent", "expected": "historical readback only, HUMAN_DECISION_REQUIRED, no start or automatic continuation" }, { "id": "crash-after-intent-before-result", "expected": "reopen retains intent, HUMAN_DECISION_REQUIRED, no automatic observation" }, { "id": "lost-response-or-ambiguous-observation", "expected": "durable uncertainty, HUMAN_DECISION_REQUIRED, no automatic retry" }, diff --git a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md index 50da5ad..7c925ad 100644 --- a/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md +++ b/docs/source-packets/wits-v0/local-owner-profile-review-synthesis.md @@ -59,6 +59,10 @@ for an uncertain observation, and route every restart to human inspection. Fresh-head Codex review then identified a contradictory clock sentence; the final repair assigns the acquisition sample to the post-lock check and the second sample to the immediate pre-observation recheck. +An additional exact-head review found that an uncorrelated monotonic sample +could not catch a stalled wall clock. The contract now records a monotonic +expiry deadline derived at acquisition and rejects the observation when either +the wall expiry or monotonic deadline is reached. These dispositions preserve the approved policy. Delegation, remote trust, external dispatch, automatic recovery/reexecution, migration and relaxed clock diff --git a/docs/source-packets/wits-v0/local-owner-profile.md b/docs/source-packets/wits-v0/local-owner-profile.md index 4dfadc4..c464707 100644 --- a/docs/source-packets/wits-v0/local-owner-profile.md +++ b/docs/source-packets/wits-v0/local-owner-profile.md @@ -23,7 +23,7 @@ and fields defined below, but no base authority or decision vocabulary. Its authority impact is limited to testing an already-valid human `start_work` decision through the fixed local observation. It removes optional recovery and all action choice, confirms the forbidden effects in this document, adds the -43 observations in the adjacent inventory, and uses the compatibility and +44 observations in the adjacent inventory, and uses the compatibility and migration rules below. Consumers MUST read this declaration before processing candidate records and reject a missing, changed, deprecated or revoked pin. @@ -141,7 +141,7 @@ members on all five are `recordKind`, `profileId`, `profileVersion`, `issuerId`, | `decision` | `humanEventRef`, `baseDecisionRef`, `baseDecisionDigest`, `requestRef`, `action`, `approvedAt`, `expiresAt` | | `claim` | `decisionDigest`, `claimId`, `attemptKey`, `successorId`, `requestRef`, `action`, `claimedAt`, `expiresAt` | | `status-event` | `eventId`, `targetKind`, `targetDigest`, `sequence`, `previousDigest`, `state`, `recordedAt`, `actorId` | -| `start-intent` | `claimDigest`, `intentId`, `successorId`, `action`, `admittedAt`, `decisionStatusHead`, `claimStatusHead`, `clockSample` | +| `start-intent` | `claimDigest`, `intentId`, `successorId`, `action`, `admittedAt`, `decisionStatusHead`, `claimStatusHead`, `clockSample`, `expiryDeadlineMonotonicNanoseconds` | | `start-result` | `intentDigest`, `resultId`, `outcome`, `observedAt`, `observationClockSample`, `observationDigest` | `outcome` is exactly `completed` or `uncertain`; absence is not an outcome. @@ -157,6 +157,14 @@ sequence is a non-negative integer; digest members use the declaration above. Th implementation proof MUST publish executable schemas matching this table before claiming candidate support. +`expiryDeadlineMonotonicNanoseconds` uses the same canonical decimal-string +representation. After the acquisition sample passes `wallTime < expiresAt`, +for both records, `effectiveExpiresAt` is the earlier of decision and claim +expiry. The verifier derives the deadline as acquisition +`monotonicNanoseconds` plus the exact non-negative difference from acquisition +`wallTime` to `effectiveExpiresAt`, converted from milliseconds to nanoseconds. +The derived value is recorded in the start intent; callers cannot supply it. + For `completed`, `observationDigest` is a digest declaration for UTF-8 RFC 8785 JCS of this exact observation envelope: @@ -231,8 +239,11 @@ acquiring the guard and again immediately before observation; wall and monotonic samples MUST NOT move backward, and the wall sample MUST NOT precede the durable prior wall sample. The initial post-lock expiry check uses the acquisition `clockSample`; the immediate pre-observation expiry recheck uses the -later `observationClockSample`. Both use `now >= expiry` with no grace period. -Unavailable clocks, invalid samples, rollback, +later `observationClockSample`. The second check denies observation when either +its `wallTime >= effectiveExpiresAt` or its `monotonicNanoseconds >= +expiryDeadlineMonotonicNanoseconds`; equality denies and there is no grace +period. This monotonic deadline makes elapsed time authoritative if wall time +stalls while the guard is held. Unavailable clocks, invalid samples, rollback, inability to read/update the durable sample, or inability to keep the same guarded process interval makes freshness unknown and blocks. No caller-controlled clock override exists outside explicit test injection, and restart never