Repository navigation
test(profiles): prove profile recovery and fenced rollback evidence - #1048
Draft
0xPlayerOne wants to merge 136 commits into
Draft
0xPlayerOne wants to merge 136 commits into
0xPlayerOne wants to merge 136 commits into
Conversation
Executable evidence for M17.02.2 (#1025), proof-only — no source changes. - tests/profile-recovery-crash-restore.test.mjs: crash/restart through the real LocalControlPlaneComposition (lease abandonment, fencing, single recovery at attempt 2, terminal outcomes never reclaimed), portable backup/restore with active-work and digest fail-closed paths, physical snapshot total-loss restore, and the hosted/self-hosted fail-closed binding matrix (no silent Hosted→local fallback). The hosted-server Postgres total-loss restore is integration-gated (RUN_DATABASE_INTEGRATION). - tests/profile-recovery-fenced-rollback.test.mjs: one attempt owner via WorkflowJobStore lease tokens, stale channel-generation rejection and heartbeat fencing, evaluateRuntimeEligibility reconnect-grant revalidation, exact rollback input binding with typed parent mismatch, interrupted/repeated operations with stored-command replay, and the interrupted local→hosted-server import rollback through applyPortableImport. - tests/profile-qualification.test.mjs: the qualification facts — profile mapping, exact wake dispatcher binding, topology/authority guard fail-closed instances, CLOUD_PROFILE_UNQUALIFIED, no implicit fallback. - docs/profile-recovery-acceptance-map.md: clause→test→result map with the per-profile supported/unproven matrix and explicit remainders. Registered additively in the smoke lane (tests/repository.test.mjs and scripts/run-bun-test-group.mjs). Ordinary sessions stay Pi-independent; no Pi-owned surfaces, recovery engines, or cross-harness transplants.
…1025) - Register the self-hosted-server PostgreSQL restore as an integration test (tests/profile-recovery-postgres-restore.integration.test.mjs) in the unsharded integration runner, the shard-3 root group, and the repository integration inventory. Disposal of every provisioned database runs after partial setup or body failure, and disposal failures surface. - Dispose every LocalControlPlaneComposition before its directory is removed, and surface failures from every release step. - Add a local graceful-drain proof: close() waits for an in-flight scheduled reconciliation pass before storage closes. - Route the local backup/restore proof through the composition's persistence. - Consolidate the adapter-level rollback fence and managed-Pi rebind proofs from PR #1046 (tests/profile-recovery-rebind-fencing.test.mjs). Its restore-binding test duplicates packages/profile-adapters and is not carried. - Remove fixture-only duplicates already pinned by package tests: tests/profile-qualification.test.mjs, lease-fencing, cancellation, import-rollback, topology, and hosted fail-closed cases. - Correct docs/profile-recovery-acceptance-map.md to the exact tests and results; drain and upgrade remain partial or unproven. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
0xPlayerOne
marked this pull request as ready for review
October 9, 2026 19:55
The drain proof held a reconciliation pass open and released it only after its assertions. A failing assertion left the pass pending and the close unsettled. Release the gate in finally and settle the drain before the assertions after it run. Verified: a deliberately failing assertion now fails the test in about 2s instead of hanging. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
The Validation integration lane runs `turbo run test:integration`, whose tasks depend only on their dependencies' builds (^build). The profile recovery restore file runs as a root-level bun test afterwards and imports @control-plane/profile-portability through its package exports (dist/), but nothing builds that package in this lane, so the import fails with "Cannot find module" and the lane goes red. Build the consumed package explicitly in the unsharded runner before the root-level files. The remote lifecycle expectation rises from three to four bun invocations to account for the build, and a new test pins that the build precedes the root-level file.
The J1 evidence poller parses process-evidence.jsonl while live child processes append to it. The CI failure for run 37985178251 reports JSON.parse rejecting an unterminated fragment at column 11, which fits a poll that reads a row before its newline is written. Only newline-terminated rows are complete, so parse those and leave any fragment for the next poll, the same way the stdout reader already buffers lines. This is a hardening change. The partial-row mechanism was not reproduced locally; the J1 concurrent-recovery flake that does reproduce is a different failure mode and is not addressed by this commit.
The J1 proof readers parsed process-evidence.jsonl while killed or live children were still appending, so a partial final row failed the poll with "Unterminated string". Both readers now share one parser (tests/process-evidence.fixture.mjs) that consumes only newline-terminated rows; a malformed complete row still throws. The child descriptor was truncated in place before it was written, so a recovery that started during a kill could read an empty file and fail with "Unexpected end of JSON input". It is now written to a temporary file and renamed over the descriptor. Adds a regression that writes a partial tail, completes it, and then rejects a malformed complete row. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Recovery claimed an interrupted record with an epoch-only compare-and-set. A recovery contender could therefore take the epoch from a live owner, and a concurrent write during the probe surfaced as a state conflict that was then recorded as authority revocation. Two parallel recoveries of one child could produce PI_PROCESS_UNEXPECTED_RECOVERY_STATE or a blocked recovery, and a run that could not start could leave a record running with no owner. - Recovery claims go through the journal's owner-liveness check. A live owner in this or another process is observed (status returned), never claimed, probed, or resumed; an unknown record waits briefly for its owner's decision. - A stale claim resamples the record. A concurrent epoch change while a claim is held is a stale result, not a revocation. - Authority is checked only under a held claim. A denial there is authoritative and is persisted as PI_RECOVERY_AUTHORITY_BLOCKED; the claim is undone so the retained record is unchanged. Any other failure is PI_RECOVERY_UNCLASSIFIED, fenced for this start and retried at the next one, with no durable revocation. - A claim that cannot be driven (lease held elsewhere, or a failure before the drive) leaves the record unknown with a reason code, never ownerless and running. - Shutdown undoes recovery claims it has not decided, restoring the epoch. Regressions cover a concurrent epoch change during probing, a Pi store lease held elsewhere, a contended record that is not classified as revoked, a live owner that is not stolen, and an unclassified failure that is recoverable. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
0xPlayerOne
marked this pull request as draft
October 10, 2026 03:47
A declared authority denial now writes its recoveryBlocked marker and advances the epoch in the same transaction that releases the recovery claim, so composition no longer writes after the claim ends. A claim superseded while its check was pending writes no marker and leaves the newer epoch alone. Typed unavailable and untyped transport failures are no longer denials: they undo the claim, persist nothing, and fence the start as PI_RECOVERY_UNAVAILABLE or PI_RECOVERY_UNCLASSIFIED. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…ansient results Add competing-recovery-owners evidence through the real composition. An old recovery owner loses its claim to a competing cancel, to another live process, or to a run-path inference authorization, then returns a declared denial, a typed unavailable failure, an untyped transport failure, or a successful probe. Each old result leaves the current owner's epoch, owner fields, record, and receipts unchanged and is reported in memory only. Restarts with transient probe failures persist no revocation marker across repeated attempts; a healthy restart resolves the competing cancellation. An orphaned settlement replayed by the current owner yields one receipt and one ledger entry, and a conflicting replay fails closed. The shared race fixture moves to recovery-races.fixture.mjs. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
0xPlayerOne
added a commit
that referenced
this pull request
Oct 10, 2026
…roduction composition The target-bound observation contract (observedTarget: server-owned session + plan task) was only proven by the isolated DurablePiDurableLeadService test (pi-durable-lead-target.test.mjs), never through the REAL production composition that installs the adapter. Adds the missing focused integration assertion: after the composed lead service dispatches, observedTarget is populated and its sessionId equals the adapter's runtimeSessionId (the actual external session), proving the composition invokes the existing adapter for target-bound identity rather than only an isolated policy mock. No canonical authorization or attempt-fencing change; no duplicate of the #1047/#1048 work. Purely additive assertion in the owned composition test. Validation: production-child-composition 1/0; control-api 482/0; runtime-sdk 101/0; policy 39/0; pi-durable-adapter 189/0; build exit 0. Refs #937, #935
This was referenced Oct 10, 2026
…ls persist Canonical authority collapsed every port, transport, and parse failure into PI_CANONICAL_AUTHORITY_REJECTED, and recovery then persisted that code as a revocation. Only explicit policy decisions are denials now: canonical reject() sites, adapter expiry, and scope checks record an outcome in a module-private registry. Every other failure is unavailable or unclassified. The public message is unchanged; only the internal outcome differs. Caller-controlled strings no longer establish a denial. A port or probe that throws a denial code persists nothing, and the adapter's denial-code matching is removed. Child-continuation denials were matched by message, so they are no longer persisted. They fail closed and need per-site classification as a follow-up. Regressions run through the production lead composition with real canonical authority and SQLite stores: transport, typed-unavailable, and forged-string outages during recovery fail closed and resume on a healthy startup; an explicit canonical lifecycle denial persists the marker under the claim; an admission outage fails closed and a healthy retry admits the intent. Expected-claim fencing tests keep their assertions and create denials through the authority. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
… reconnects Replace the fake-transaction acceptance evidence for channel-generation fencing with a real PostgreSQL proof. Two owners compete for generation 1 over independent physical sessions, the loser advances the channel to generation 2, and the dropped winner reconnects over new sessions. Its heartbeat, release, claim, and inventory effect are refused, and the generation-2 inventory effect runs exactly once. Effects run through PostgresRuntimeInventoryUnitOfWork, and sessions are terminated server-side between steps. The profile fenced-rollback smoke file drops its fake-seam block, and the acceptance map cites the PostgreSQL proof and states that a live gateway handshake remains unproven. The reviewed shard inventory gains this one foundation case: 66 cases, with shard 1 at 38 of its 45 allowed. Fence-removal mutants fail the proof. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…the composition Drive the hosted-simple composition with the pinned @restatedev/restate-server. A graph approval paused in Restate is closed and reopened, and separately restored from a filesystem checkpoint. The reopened composition keeps the execution identity, attempt, and plan record; refuses a superseded execution version; starts no second effect from a duplicate run submission; and resumes the approval exactly once. The portable backup refuses active work, exports from the composition's SQLite persistence, and restores into a fresh composition after total loss. Registered once in run-integration-tests, integration-shards, and the repository integration inventory. The completed managed-Pi client moves to a shared fixture that the standalone suite imports. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
releaseProcess matched any owner recorded by this pid, so two adapters in one process could let a late cleanup from an earlier claim clear a later owner. claimProcess now returns a token (command epoch, owner pid, owner epoch, claim id). Every release and abandon requires the token it holds, and the claim id keeps a reissued epoch from matching an earlier claim. Detail writes cannot erase or replay stored owner fields. A run ends its claim exactly once: a failed engine close still releases the claim, and the scheduler no longer issues a second cleanup. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…n and cancel Observation and cancel under a v2 fence now run on the retained authority instead of failing closed. A ready marker must match the fence on authority revision, scope, allowed principals and canonical actor, and cancel is limited to the original actor. The receipt is verified against the retained admission, request and handle, and the admission digest is re-derived with the canonical function, so the withheld prompt is never re-read. Current authority is rechecked after each awaited effect. Prepare and dispatch stay refused under a fence. Legacy markers that lack a canonical actor pin are refused. Admissions without an explicit scope now retain the canonical actor they were given. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…live in-flight run - lead-fence-live-harness.mjs: shared fixture over the production lead composition, real SQLite, the real Pi adapter, and a scripted HTTP model whose turn is held open until the test releases it or the client aborts. Receipts and admission digests are read and written as they are; no journal state is rewritten. - lead-fence-service.test.mjs: fenced status, progress, and original-actor cancel against the in-flight run, with zero extra starts, inferences, or reservations; changed pins, outsider and second-principal denials; the configured preparation path; receipt tamper refusals; recheck after awaited effects. - lead-fence-late-authority.test.mjs: deterministic late-authority cases (fence revision, execution deadline, retained expiry, scoped execution-scope read) and the superseded-attempt qualification. Current behavior that is a gap is asserted and named as a gap. No production code changed. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…d Restate child Adds a test-only proof: the supported LocalControlPlaneComposition (profile hosted-simple, durable execution restate) runs in a child process, which is SIGKILLed while its pinned Restate launcher and native server run. The parent records both identities before the kill, verifies they are orphaned and alive, reaps exactly those identities, and starts a second composition on the same disposable data. The paused approval keeps its execution, attempt, and version; a duplicate run submission starts no second effect; one approval resumes the workflow; prepare runs once, finalize once, and one result artifact is written across both incarnations. A second test shows reaping never signals a reused pid. Production code is unchanged. The helper and proof are separate files; the shard, runner, and inventory registrations list the new integration file. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…wn errors, and cleanup Protocol: stdout records carry an explicit prefix. Non-protocol JSON, bare protocol-shaped lines, and malformed records are ignored and never throw from the stdout handler. Spawn: spawn errors resolve the exit promise and reject protocol waits with PROTOCOL_CHILD_SPAWN_FAILED instead of hanging. Bounded waits: every exit wait is bounded. Cleanup SIGKILLs and awaits each spawned composition incarnation and the bystander before removing the data directory. Scope: process control reads only recorded identities (ps -p <pid>) and direct children of the recorded launcher (pgrep -P). The full process-table listing is removed. Paths: the test file's repositoryRoot now uses a single parent step from tests/. Labels: this proof uses the legacy LangGraph test composition, not Pi Durable host wiring. The parent reaps the orphaned Restate processes manually before restart. It does not adopt orphans. Production code is unchanged. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…t confirm exits Cleanup now attempts every spawned composition and every recorded Restate identity, even after an earlier failure. Each unconfirmed child or identity is reported with its pid and start time. reapOwned no longer splices the ledger up front. A confirmed identity leaves it; a failed one stays with its reason, and one retry covers only the identities still recorded. The work directory is removed only when nothing remains unconfirmed. Otherwise it and the recorded identities are kept, and the error names the directory. A composition whose start or failure record never arrived is reported as RESTATE_OWNERSHIP_UNRECORDED, since its Restate processes were never recorded. A focused test drives releaseOwnership with a child that never exits and one that does: both are attempted, the error is aggregated, and the directory remains. A live identity that survives SIGKILL cannot be produced safely in a test, so the retention branch is covered by review only. This proof remains the legacy LangGraph test composition with a manual orphan reap before restart. It is not Pi Durable host wiring and does not adopt orphans. Production code is unchanged. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
… proof The map said self-hosted simple SIGKILL recovery was unproven. The SIGKILL proof (tests/profile-recovery-self-hosted-simple-sigkill .integration.test.mjs) now exists. It runs the legacy hosted-simple composition and reaps the orphaned Restate child manually before restart, so product startup does not adopt orphans. - Add the SIGKILL test to the ownership table, with its legacy scope. - Update the clause map, per-profile matrix, and gaps 3, 4, 7 to match. - Add gap 8: Pi Durable startup recovery and orphan adoption are unproven at composition level. Name the startup entry points and the fixture that would close it. Adapter-level SIGKILL proofs exist, but no test kills the lead composition and starts a fresh one. - Give every remaining gap an existing owner issue or say that none is open. Docs only. No source or test changes. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* chore(deps): update dependency @opentelemetry/sdk-metrics to v3
* chore(deps): keep @opentelemetry/sdk-metrics on the 2.x line
The Renovate major to @opentelemetry/sdk-metrics 3.0.0 cannot be taken under
this repository's API policy. sdk-metrics 3.0.0 depends on
@opentelemetry/core 3.0.0 and @opentelemetry/resources 3.0.0, and all three
declare a hard peer requirement of @opentelemetry/api ">=1.10.0 <1.11.0".
The repository pins @opentelemetry/api at exactly 1.9.1 in packages/telemetry,
apps/control-api and apps/workflow-worker, and packages/telemetry declares the
optional peer "@opentelemetry/api": "^1.9.0". The graph resolved by this
branch carried a single @opentelemetry/api 1.9.1 and no nested 1.10, so those
three peer requirements were unsatisfied.
Moving the api to 1.10 would break the same policy elsewhere:
@sentry/node 11.4.0 depends on api "^1.9.1", @sentry/opentelemetry 11.4.0
peers on "^1.9.0", and vitest 5.0.5 peers on "^1.9.0". Moving the api is a
repository-wide upgrade rather than a dependency update, and it would also
require widening this package's own declared peer range.
2.12.0 is the newest release whose peer requirement (">=1.9.0 <1.10.0") the
pinned api 1.9.1 satisfies, so the dependency stays there and the lockfile
returns to the 2.12.0 resolution. The lockfile is otherwise unchanged from
main: `bun install --frozen-lockfile` reports no changes against it, so the
install churn the update carried is not kept.
Verified with the existing dependencies: packages/telemetry test 42 pass / 0
fail, and build, typecheck and lint clean.
* chore(deps): take @opentelemetry/sdk-metrics 3.0.0 with @opentelemetry/api 1.10.0
The Renovate major to @opentelemetry/sdk-metrics 3.0.0 is taken together with
the API move it requires. sdk-metrics 3.0.0 depends on @opentelemetry/core
3.0.0 and @opentelemetry/resources 3.0.0, and all three declare a hard peer
requirement of @opentelemetry/api ">=1.10.0 <1.11.0", which the pinned api
1.9.1 did not satisfy.
The api moves to 1.10.0 in the three manifests that pin it - packages/telemetry,
apps/control-api and apps/workflow-worker - because the graph resolves one api
copy for the whole workspace. Nothing else in the tree blocks that:
@sentry/node 11.4.0 and @sentry/server-utils 11.4.0 depend on api "^1.9.1",
@sentry/opentelemetry 11.4.0 and vitest 5.0.5 peer on "^1.9.0", and
drizzle-orm 0.45.3 peers on "^1.4.1"; every one of those ranges admits
1.10.0. This package's own declared peer range stays "^1.9.0", which also
admits 1.10.0, so no peer policy changes.
The lockfile is regenerated from the published packages: api 1.10.0,
sdk-metrics 3.0.0, core 3.0.0 and resources 3.0.0, and exactly four of the
598 resolution entries change. No override, resolution or peer rule is added.
Checked with the upgraded graph: packages/telemetry test 42 pass / 0 fail with
build, typecheck and lint clean; apps/control-api and apps/workflow-worker
build and typecheck clean, with 700 and 158 unit tests passing; the repository
lint and format checks pass; and a fresh `bun install --frozen-lockfile` leaves
the lockfile byte-identical and resolves a single api version, 1.10.0.
…tory (#1208) * fix(ci): qualify the task cache from the real output inventory The lane proved a cache hit by grepping the runner log for '45/45 cache hit': a hardcoded task count matched against a human-facing string, so a changed build graph could keep passing or start failing for the wrong reason. The lane now reads the task runner's own machine-readable run summary and compares it with the build graph, so the expected task set, the expected commands and the expected outputs all come from the graph itself. It also compares the build outputs of a clean build with the outputs of the cached replay, byte for byte, and it checks that the cache holds one entry per cached task the graph declares. A malformed summary, a missing or unexpected task, a command that does not match the graph and an output inventory that differs from the clean build all fail closed. A changed source, a changed configuration input and a changed toolchain each have a fixture. * fix(ci): make the task cache qualification fail on what it compares Four defects in the qualification, all found in review: The compare command reported equal:false for differing artifacts but the command line only failed on a failure list or ok:false, so an inequality passed. It now reports the difference as a failure and exits non-zero, and subprocess tests cover a changed, a missing and an extra output. The expectation evaluation returned success immediately when no cache pattern was given, so the semantic checks were skipped: a run that did not execute the expected tasks, that executed tasks outside the graph, that ran a command the graph does not declare, that failed a task or that exited non-zero passed. Those checks now hold for every run, and the command line without a cache pattern is tested. The lane compared the outputs of a build that could inherit files left on disk with the outputs of a clean build, and never asserted that the inventory covered anything. The declared outputs are now removed before every build the lane compares, only the outputs the build graph declares are removed, and an empty inventory or an empty output coverage fails. The source probe left an entry for the changed source, so the following configuration probe could not attribute its miss to the configuration. The source baseline is reseeded between the two mutations, and the configuration probe now has to name the configuration input that changed instead of accepting any invalidation. * test(ci): register the task cache qualification test in the smoke lane The shared test inventory and sharding runners are owned outside this lane. The root-reviewed artifact from that owner, library file libfile_6547c0e2e4ac819191e8ed8a579b004e, SHA-256 b7119105f4ba7ea5d92fdf21b2f4791df35de1eb33e4103319e6d01d99d237c7, declares base ae372d4 and adds exactly two registrations: - scripts/run-bun-test-group.mjs claims tests/task-cache-qualification.test.mjs in the smoke lane. - tests/repository.test.mjs lists the same path in its expected discovery inventory. Without the registration the shared inventory guard fails closed with "Test lane inventory drift: unowned [tests/task-cache-qualification.test.mjs]", which failed the smoke lane and the two aggregate gates. The artifact was applied with git apply --index after its checksum, its declared base and both file preimages were verified byte for byte. No guard, assertion, threshold or sharding behaviour changed, and no other file was touched.
…rent-main worktree
…eneration The native cancellation boundary states that matching installation evidence never proves complete writer coverage. This qualification proves the coverage is complete and proves two properties independently: no writer that accepts a target or an epoch accepts a rewound one, and the restore generation is bound into the request digest so a restore cannot re-point it and replay the request. The writers are enumerated from the source, so a writer that starts accepting a target or an epoch without a proof here fails the file.
…nto the isolated current-main worktree
…#1048 acceptance ledger The writer-coverage qualification closes the restore-generation and all-writer sub-gaps that the native cancellation boundary states it never proves. The ledger records what the integrated source proves, what remains unproved, and which gate proves what, including the native zero-effect, SDK adoption, installed cross-repo/profile acceptance and production activation holds.
CP #678: a deployment can now map a model id to the harness that must serve it, so "deepseek harness for deepseek-flash tasks, ZCode for GLM models" becomes a deployment policy instead of a single deployment pin. The carrier is the execution read model, not the execution plan: the execution carries the accepted model (ExecutionSchema.modelId, nullable in the row so historical executions replay exactly), and the execution plan and its content digest are untouched. The public acceptance request takes the optional accepted model, the command inbox records it, and the Postgres and SQLite projections carry it. The map lives beside the deployment pin (ManagedCloudRuntimeConfiguration. harnessByModel, CONTROL_PLANE_HARNESS_BY_MODEL) with strict model-id and harness-id validation; an absent map leaves routing exactly as it is, and a present map is a policy rather than a fallback. At attempt time the router resolves the mapped harness from the execution's model and passes it through the same pin path, so a mapped model reaches only a runtime exposing the mapped harness, the mapped harness is bound into the routing digest, and the reason code HARNESS_MAPPED records that the map decided it. With a map configured an execution without a model denies with HARNESS_MAP_MODEL_ABSENT, a model the map does not name denies with HARNESS_MAP_MODEL_UNMAPPED, and a deployment pin that names another harness denies with HARNESS_MAP_CONFLICTS_WITH_PIN: the explicit harness choice and the explicit model stay independent and nothing is silently substituted. The decision layer applies the same map to an explicit session or workspace harness pin. The map is wired in the real managed cloud worker composition, not in a helper. The hosted control-plane composition and the execution acceptance service that forwards the accepted model are owned by another lane, so those two seams are handed off rather than co-owned.
The lane ran 359 pass and 12 fail across 40 files. Every failure is in the self-hosted-simple SIGKILL proof, which fails closed on macOS because the pinned-process fixture needs a Linux pidfd. That file and its fixture are byte-identical to the published #1048 head, and the CI runs the lane on ubuntu-latest. The ledger now states the exact count and the limit.
…kups Two defects in the per-model harness map, both in the configuration and mapping path this lane owns. The map loader carried its own id patterns, so the configuration could name an id the execution contract would refuse and the entry could never be reached. It now validates the model ids and the harness ids with the canonical ModelIdSchema and HarnessIdSchema, so the configuration accepts exactly what the contract accepts and the two can no longer drift. The mapping rule read the model id straight off the map, so a model id such as `constructor` or `toString` resolved an inherited object property instead of being unmapped. The lookup is now an own-property lookup and the configuration builds a null-prototype map, so an inherited name is either an ordinary configured key or unmapped. The accepted read-model carrier, the absent-map compatibility, the no-fallback denial, the provider/model independence, the explicit harness conflict rejection, the pinned and exposing-runtime checks and the unchanged execution plan digest are all unchanged.
…generation gap The previous qualification overstated what it proved. Three corrections. A digest over a supplied restoreGeneration proves tamper consistency only. The old test changed the generation while retaining its old digest, which missed the rollback that restores the generation and recomputes a matching digest. The qualification now proves that the pure contracts accept that rollback, so the limit is recorded rather than papered over. No independent generation authority exists in the current architecture, and the full-writer restore-generation acceptance stays open. The old registry covered only the two cancellation modules and excluded the effect gate, journal, lease, upgrade manifest and resume permit writers, so it could not substantiate complete coverage. The writers are now enumerated from the source and classified by the authority each one actually holds. A rewound epoch is refused where the architecture grants one, and the five writers with no independent generation authority are named. The vacuous safeParse not.toThrow assertion is replaced with assertions on safeParse().success and on the intended binding properties.
… lane failures The upgrade manifest is named in the writer universe, so it now carries a real restore-state test: it detects a store that was rolled back to different bytes by byte identity. That is byte identity, not a generation, and the gap stays open. The acceptance ledger now records the aggregate unit lane exactly: the Bun 1.4.3 panic and 7 test failures, and that the same failures and the same panic reproduce with the writer-coverage file held aside, so they are pre-existing on the integrated tree.
…branch The CP #1210 contract is consumed as delivered by its own branch: the modelId carrier on the acceptance request and the acceptExecution command, the execution read-model projection, the per-model harness map policy, the deployment configuration, and the runtime attempt router. No W2-owned path is edited here; the merge brings their accepted contract forward unchanged. The CP #1048 history and patch are preserved: this is a fast-forward for the published #1048 branch, and no commit is rewritten.
…router The CP #1210 contract puts the accepted model on the execution read model and maps it to a harness through the deployment's per-model map. This wires the production seams that carry it there. The control API acceptance service forwards the accepted request payload's modelId into the acceptExecution command, so the execution read model records the model the caller accepted. The hosted control plane composition passes the per-model map to the real worker router, and its index resolves the map from the deployment environment with the same strict parsing the contract uses: an absent variable leaves routing unchanged, and a present map is a policy that denies an execution it does not name. The new end-to-end qualification drives the real chain over HTTP and the real hosted router: HTTP acceptance, the persisted execution read model, and the router carrying the accepted model to its mapped harness. It covers the configured map, an unmapped model, an execution with no model, a pin that conflicts with the map, the no-map behavior, and the unchanged execution plan digest. The CP #1048 restore-generation authority gap stays open and is not closed by this work.
… inventory The hosted model-forwarding integration test was discovered by the integration file inventory but had no local shard owner, so the disposable-local PostgreSQL shards did not run it. It is now registered on shard 3, next to the other hosted-control-plane integration files, and every existing entry is preserved. This is the local sharded registration. The hosted-control-plane package's own integration glob already matches the file, and that is a different registration: the package script runs the package's files directly, while the local sharded lane runs this explicit inventory. The inventory is what gives the local lane its coverage, so the regression asserts the shard owner, the arguments the local runner uses, and the package glob separately. The inventory regression is negative-proven: removing the registration fails both the file-ownership check and the new registration check.
…ation inventory The Code Foundry group-discovery expectation list did not name the hosted model-forwarding integration test, so the exact-list assertion failed at the owner head even though the file was discovered correctly. The file is now in the expected inventory, in its sorted position, and every existing entry is preserved. The new focused regression states this file's registrations separately: it is discovered as an integration test, it appears exactly once in the inventory, it has exactly one local shard owner, and its package integration command selects it. The package integration lane and the local sharded lane are distinct registrations of one file. Both assertions are negative-proven: removing the expected-inventory entry fails the exact-list assertion, and removing the shard registration fails the focused regression.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Current custody — integrated CP #1204 + #1048 backend outcome —
7d599a7d2b48bc5ebfc0a8a125f6fd335df1aec6Draft, auto-merge OFF. Root retains readiness and merge. No readiness, merge, issue closure, native qualification, SDK publication, installed acceptance or production activation is claimed.
This head integrates the complete existing CP #1204 + #1048 backend outcome onto current remote
main(27f17bd55e0266439b5b3089163195c9e7a669be) and closes one concrete gap inside it.Custody and preservation
253b4ada58e3560fedef142fc7cbd926ed5662ce— unchanged branchfeat/pi-durable-completion-20261020, now also addressable asrefs/cp/pr/12047cf6d67e7175fa25bc4012f666aa02c53819335e— an ancestor of this head, now also addressable asrefs/cp/pr/1048mainat27f17bd55e0266439b5b3089163195c9e7a669be1c6cb7d2b..7d599a7d2onfeat/profile-recovery-rollback-1025. No force push, no history rewrite, no deletionThe
#1048branch already contained the#1204head as an ancestor, so the integrated outcome is published here. The#1204branch is preserved untouched at its published head.The corrections root required
Root's complete review of
ae9cbb812found that the writer-coverage qualification overstated what it proved. This head corrects all three points.1. A digest over a supplied
restoreGenerationis tamper consistency only, not restore authority. The previous test changed the generation while retaining its old digest, which missed the rollback that restores the generation and recomputes a matching digest. The qualification now proves that the pure contracts accept that rollback: the retained intent schema accepts the rolled-back body with its recomputed digest, the permit schema accepts a permit over it, the permit snapshot accepts it when the current fence agrees, and the native receipt binding accepts it. The assertion is non-vacuous — inverting it fails the file.Why: nothing in the pure contracts holds an independent generation authority.
restoreGenerationis a caller-supplied reference, the onlyPiCancellationOwnerPortimplementation isUnavailablePiCancellationOwner(it throwsPI_CANCELLATION_AUTHORITY_UNAVAILABLE), and no production consumer reads a current fence to compare it against the target's generation. Establishing independent, non-rewound restore-generation authority needs production authority that does not exist in the current architecture. The full-writer restore-generation acceptance remains an explicit open gap, recorded indocs/pi-durable-complete-writer-coverage.mdand in the acceptance ledger.2. The writer universe now covers every writer of the durable state. The previous registry covered only the two cancellation modules and excluded the effect gate, journal, lease, upgrade manifest and resume permit writers, so it could not substantiate complete-writer coverage or a claim that no writer accepts a rollback. The writers are now enumerated from the source and classified by the authority each one actually holds:
cancellation-owner.tsnative-cancellation-boundary.tsjournal.tsSTALE_STATE; a released claim no longer holds the recordeffect-gate.tslease.tsresume-permit.tsupgrade-manifest.tsThe five writers that carry no independent generation authority are named, not excluded. The enumeration is derived from the source, so a module that starts writing the durable state without being classified fails the file.
3. The vacuous
safeParse().not.toThrowassertion is gone. EverysafeParseresult is asserted on.success, and the intended binding properties are asserted directly.The CP #1210 production integration
This head also carries the complete CP #1210 backend outcome, consumed as delivered by its own published branch
95021f73a1127d117317ed78e5d7466f3ad61783(PR #1210, still open as a draft). That contract is merged forward unchanged: every W2-owned path is byte-identical to W2's published head, and no W2-owned file is edited here.The production seams that carry the accepted model are W1-owned and now wired:
apps/control-api/src/executions/execution-acceptance.service.tsforwards the accepted request payload'smodelIdintocommands.acceptExecution, so the execution read model records the model the caller accepted.apps/hosted-control-plane/src/composition.tspasses the per-model map to the realRuntimeDiscoveryAttemptRouter, andapps/hosted-control-plane/src/index.tsresolves it from the deployment environment with the contract's strict parsing. An absent variable leaves routing unchanged; a present map is a policy that denies an execution it does not name.The new end-to-end qualification
apps/hosted-control-plane/src/hosted-model-forwarding.integration.test.mjsdrives the real chain over HTTP against the real hosted router: HTTP acceptance, the persisted execution read model, and the router carrying the accepted model to its mapped harness. It covers the configured map, an unmapped model, an execution with no model, a pin that conflicts with the map, the no-map behavior, and the unchanged execution plan digest. The provider/model independence, the exposing-runtime and pinned-harness guards, the auth and tenant boundaries, and the historical replay semantics are unchanged.The qualification is a source integration proof. It activates nothing: no provider is called, no native effect is performed, and no production resource is touched. The workflow runtime is an external service, so the acceptance path's post to its ingress is answered by a minimal local stub; nothing else about the runtime is exercised.
The CP #1048 restore-generation authority gap stays open. This work does not close it, and the writer-coverage qualification still records that a generation rollback with a matching digest is accepted by the pure contracts.
The narrow CI inventory repair
The hosted model-forwarding integration test was discovered by the integration file inventory but had no local shard owner, so the disposable-local PostgreSQL shards did not run it. This head registers it on shard 3, next to the other
apps/hosted-control-planeintegration files, and every existing entry is preserved (63 entries before, 64 after, none removed).Two registrations are distinct, and the repair covers only the one that was missing:
scripts/integration-shards.mjs. This is what the disposable-local PostgreSQL shards read, and it did not name the file. It is now registered.apps/hosted-control-planerunsbun test --timeout 30000 src/*.integration.test.mjs, which already matches the file. This registration was already present and is unchanged.postgres-pull-requestworkflow runs the dedicated local-DB qualifications and the shard suite; it did not name this file and is unchanged.The inventory regression in
tests/integration-shards.test.mjsasserts the shard owner, the exact arguments the local runner uses for the file, and the package glob separately, so the two registrations cannot be conflated. It is negative-proven: removing the registration fails both the file-ownership check and the new registration check.Only
scripts/integration-shards.mjsandtests/integration-shards.test.mjsare edited. No other path, owner, shared runner, production resource or credential is touched.The focused light checks at this head
bun test tests/integration-shards.test.mjsbun test tests/repository.test.mjsbun scripts/workspace-boundaries.mjsbun scripts/check-locale-compare.mjsbun scripts/check-digest-writers.mjsoxfmt --checkon both edited filesoxlinton both edited filesThe
tests/repository.test.mjsfailure (discovers disjoint Bun test groups for Code Foundry) is pre-existing at the owner headf4310854dwith a clean tree — it reproduces with this repair's edits stashed, and its failure signature is byte-identical before and after this change. Its cause is that the Code Foundry group-discovery expectation list in that file does not name the hosted model-forwarding test, which the earlier CP #1210 commit added. That file is outside the two paths cleared for this repair, so it is reported rather than edited. It needs one line added to the expected integration list:Only the focused light checks were run for this repair. No aggregate heavy lane was run.
The repository integration inventory follow-up
The narrow CI inventory repair registered the hosted model-forwarding test in the local shard inventory, but the Code Foundry group-discovery expectation list in
tests/repository.test.mjsstill did not name it. That exact-list assertion therefore failed at the owner head1c6cb7d2bwith a clean tree, even though the file was discovered correctly.This head adds the file to the existing exact expected integration inventory, in its sorted position, and adds a focused regression. Every existing entry is preserved: 60 entries before, 61 after, none removed, and the
assert.deepEqualexact-list assertion itself is unchanged.The focused regression states this file's registrations separately:
Both assertions are negative-proven: removing the expected-inventory entry fails the exact-list assertion, and removing the shard registration fails the focused regression.
Only
tests/repository.test.mjsis edited. The shard files are untouched, and the registered historical detached worktree with its staged unrelated inventory entries was neither modified, staged, reset, nor otherwise touched.The focused light checks at this head
bun test tests/repository.test.mjsbun test tests/integration-shards.test.mjsbun scripts/workspace-boundaries.mjsbun scripts/check-locale-compare.mjsbun scripts/check-digest-writers.mjsoxfmt --checkon the edited fileoxlinton the edited filePre-existing failures, preserved and reported separately: the
tests/repository.test.mjsfailure that this head fixes was pre-existing at the owner head1c6cb7d2bwith a clean tree, and it is now fixed. No other failure was present in the focused light checks at either the owner head or this head.Only the focused light checks were run. No aggregate heavy lane was run, and no readiness, merge, issue closure, production change, or credential action was taken.
The aggregate unit lane: the exact failure baseline
The lane is the shared runner
scripts/run-bun-test-group.mjs, which is not W1-owned. Its failures are handed to their owners with the exact command and logs. No threshold, check, hook, timeout or assertion is waived, suppressed or weakened, and the full suite is not called green.The lane is load-sensitive: the pristine published head
ac143c19dproduced 7 failures plus the Bun panic in one run and 29 in another, with no source change between them. At this head it produces 12 entries (11 failures plus the same panic). Every failure in the pristine baseline also fails here. Three appear at this head that appear in neither baseline run —actual process exit and restart retain native tool source and approval checkpoint without replaying inference or settled effect,distinct workspace processes retain isolated input receipts despite shared idempotency keys and one process restart, andoperator CLI provisions scoped grants and registrations and preserves revocation. All three are outside the CP #1210 scope, and all three pass in isolation at this head (11 pass / 0 fail and 6 pass / 0 fail). They are load-sensitive timeouts in the same family as the 22 extra baseline failures, not regressions from this repair.The Bun panic (
range end index 1884 out of range for slice of length 1024) is W4's active CP #1030 isolation scope, not W1's.The focused qualification at this head
The model-forwarding qualification is negative-proven: removing the model forwarding from the acceptance breaks 4 of its 6 cases.
Qualification at this exact head
tests/profile-recovery-self-hosted-simple-sigkill.integration.test.mjs:PINNED_PROCESS_UNSUPPORTED: platform darwin has no pidfd, plus theRESTATE_OWNERSHIP_UNRECORDEDconsequence. That file and its fixture are byte-identical to the published #1048 head, and the CI runs this lane onubuntu-latestonlyThe proofs ran on the repository's pinned toolchain (
bun@1.4.3,node@24.21.0,darwin-arm64) against a disposable local PostgreSQL from the repository's owncompose.yamlon127.0.0.1:54329, using the repository's three distinct roles. That database was created for this qualification and torn down with it. No account, credential or production resource was touched.What remains unproved
range end index 1884 out of range for slice of length 1024) and 7 test failures: the runtime-gateway host credential startup case, the two qualified-cancellation deadline and revocation cases, the crash-at-admission-boundary case, and the three canonical recovery-outage cases. The same 7 failures and the same panic reproduce with the writer-coverage file held aside, so they are pre-existing on the integrated tree and not caused by this qualification. The Bun panic is W4's active CP M17.03.1 — Correlate runtime telemetry and measure operations #1030 isolation scope.ubuntu-latest, where the proof is intended to run.Custody boundaries observed
W4's CP #1030 lifecycle, architecture, inventory and shared-runner paths were not edited. The only shared-runner edit is the mechanical merge resolution that keeps both sides' files. Sol's shared runner and inventory surfaces carry no feature work from this outcome. The published branches, the stale preservation refs and the untracked
.bun-cacheare preserved.The full acceptance ledger is in
docs/pi-durable-cp1204-1048-acceptance-ledger.md; the writer-coverage scope, its corrected findings and the open restore-generation gap are indocs/pi-durable-complete-writer-coverage.md.Current retained-intent review repair —
7cf6d67e7175fa25bc4012f666aa02c53819335eDraft, auto-merge OFF; fresh root exact-head review required. Three files changed; no native capability or production behavior was activated.
assertPiNativeCancellationReceiptBindingnow parses its retained-intent input withPiCancellationRetainedIntentSchemabefore timestamp/digest comparisons. Two regression-first negatives cover malformedrequestedAtand changedidempotencyKeyretaining the old request digest: both failed before the fix and pass afterward. Older drain-before-ACK protocol/outcome prose is explicitly marked superseded history and points to currentF<A<R, permittedN<F<R<Dsemantics.Exact combined contracts plus repository/inventory/shards:83 pass,351 assertions,5 files,2.41s. Unchanged aggregate type-check97/97 plus schema/compatibility/requirements/architecture/infra pass. Full formatting:1,991 files pass. The matching CP1204 repair passed the full affected Pi package:262 pass,1,662 assertions.
Native engine/composition, shared Adea writers, SDK and original zero-effect gate file are unchanged. No native qualification is claimed: the prior executed combined native gate receipt remains4 pass/2 fail; that file was not rerun for this pure validation/docs repair. All-writer/restore-generation/installed acceptance remains pending. No review approval, readiness, merge or issue closure is claimed.
Prior exact-head source and qualification receipts (historical)
Current dormant native-boundary slice —
1272e45e790d465034230095324b9f204f434d25Draft, auto-merge OFF. Fresh complete exact-head root review is required. No readiness, merge, native qualification or migration completion is claimed. Scope remains adea-ai/adea#1173 and the preserved CP migration/recovery work; no aggregate issue is closed.
This four-file slice adds strict native preparation/target/receipt and mutation-owned fence-installation contracts, pure evidence-binding comparisons, negative tests and documentation. The default port rejects every prepare/commit/install/read/drain operation. No adapter/engine/composition injection, SDK installation, Context/Storage proxy, shared Adea authority writer, restore proof, permission or production change is included. Preparation is neither permission nor a frozen in-memory cut: an eventual N must reread persisted scope/task/input/ownership transactionally. Lost-response publication/drain must resolve the exact persisted generation and never abort a successor.
Andrew approved unavailable/not-confirmed revocation when runtime fencing cannot be confirmed and post-R drain D for cancellation N committed before R. Required F<A<R and permitted N<F<R<D remain; no new old-epoch N after F and no transaction gate held through D. Handshake B does not imply bounded end-to-end success/response.
Current source validation
Exact combined contracts plus repository/inventory/shards:81 pass / 349 assertions across5 files. Aggregate type-check97/97 plus schema/compatibility/requirements/architecture/infra passes. Full format check:1,991 files pass. Unchanged cross-store native cancellation gate file:4 pass / 2 fail,38 assertions; final revocation/deadline requirements still receive PI_LEAD_CANCELLATION_OUTCOME_UNKNOWN.
Source lint/build/format, boundary, canonical-order and digest-writer checks pass. Full smoke was not rerun for this inert slice; earlier b9/e93 smoke receipts below are historical exact-head results. Released/installed SDK, authenticated product timeline, native atomic cancellation/all-writer/restore-generation and installed Mac acceptance remain open.
Actual public-facade qualification, not native-effect acceptance
The additive guarded-abort contract remains the reference. Selective native operation identity, exact persisted scope/zero-write handling, all required writer closure and independent restore generation are not qualified. No current outer check or retained request is described as atomic cancellation permission.
Historical exact-head receipts (not current-head acceptance)
Preserved reconciliation published —
e93f29cde469504063227b373ec6cbb7646afbdfDraft, auto-merge OFF; fresh complete exact-head root review and acceptance remain required. No issue is closed.
This is a fast-forward from old published
e635101384587b6d9e66dd6d1dbfb0eec8666883. Its preserved history now includes current mainf4f17194e04bcf8af65e8b5ff7aed7926b51ca88and exact #1204b9ea19ddc3986b9641cc63651109f217f432731b. All nine original source conflicts are resolved. Old clean checkouts, custody archives and partial integration records were preserved; no archived session was revived and no force push used. The final ancestry-only merge has the same source tree as tested merge11d70aa0b2d61f9d76f2d4895122b780e7493a11(bd1ab9cf7383b02bff886c34876a0f9fa9626d44).Reconciliation decisions
docs/pi-durable-committed-cancellation-authority.md.Validation
macOS arm64, Bun1.4.3 / Node24.18.0, PostgreSQL18.6 disposable private cluster, October20 UTC. No paid providers, credentials/grants, deployment, package publication or production action.
PI_LEAD_CANCELLATION_OUTCOME_UNKNOWN, required rejection before effect. No skip, weakened assertion or timeout increase. Current native Pi public API/all-writer protocol does not provide the qualified atomic effect fence.Precise exact-head native effect gate observations
The complete Pi+lead/fence suite was rerun on this exact e93 source: 410 pass /2 fail,2,543 assertions,412 cases across39 files,33.86s. The executed failing names are:
qualified cancellation must reject revocation after its final asynchronous authorization with zero effectqualified cancellation must reject deadline change after its final asynchronous authorization with zero effectSeparate observation using the unchanged disposable local live harness confirmed both: final callback entered; response
PI_LEAD_CANCELLATION_OUTCOME_UNKNOWN; journalcancelling; cancellation intent retained; provider streams aborted 1; physical requests 1; starts 1; reconciliation calls 0. Thus this is a demonstrated post-final-callback effect-authority gap, not missing metrics or mere review bookkeeping. Zero-effect rejection is still required. No finite-read atomicity claim, gate weakening, coordinator activation or irrevocable-grant restoration is accepted. Native commit-time compare-and-cancel plus the all-writer/restore-generation protocol remains gated.Exact paired local package consumer qualification
CP e93f29c, clean source digest
sha256:a9e30b837df54f4784529a37b4f1984fe0fcf068885114be6d76953d935f8776, was locally packed using the existing no-publication script and installed into disposable consumers from Adea a4a5059b115530b0002ada9400a24770e430294a. Both actual lead and model consumers passed their TypeScript checks and fixture suites. Adea source and repository dependency pins were unchanged. Exact archive SHA256:8b2f536bb39b313e31576c8b658f9daf67957c85ee93f5ffd8739330ead0b18020e467843f11b9b84ac79eab53fb949c8a80aec57c5fd4dfdb03629b7b431d5ced9c35eaf0e93620968977d18ed8b90006b6a1221608a8c11746bf5b473a64a5These are local candidate manifest versions, not registry releases. No connected PostgreSQL, authenticated Adea timeline, device or desktop acceptance follows. The connected runner requires an existing qualified loopback Adea fixture and shared light-lane handoff; this session owns only the stopped UNIX-socket PostgreSQL fixture. Root/database-owner coordination must supply that authorized target/handoff without new credentials or access grants.
The distinct #1204 b9 head also now passed its own unchanged full smoke:595 pass,2 existing conditional PostgreSQL skips,0 fail,5,311 assertions, seed1104,117.65s. Both smoke results supersede their own prior timeout receipts without establishing a timeout cause.
Remaining acceptance gates
Native commit-time compare-and-cancel plus all relevant authority writers; independently qualified restore generation/cross-store restore; production trusted no-send authority; real supported release-pair upgrade/rollback; installed released SDK/runtime+authenticated Adea reader/canonical timeline+Mac desktop/Railway combined acceptance. UI implementation belongs to Claude and is not touched. No retained intent or post-effect unknown response closes these gates. Full migration remains incomplete.
Refs adea-ai/adea#1173, #1025, #942. Current reconciliation map · Historical100-path ledger.
Earlier receipts (historical; not current-head acceptance)
Current published head and reconciliation status
Published head:
e635101384587b6d9e66dd6d1dbfb0eec8666883, branchfeat/profile-recovery-rollback-1025. Draft; auto-merge off; conflicted with current main. The historicalb145dereceipt below is not exact-head qualification of this published source. No issue or PR is complete from this reconciliation.Sole Pi owner has begun isolated reconciliation with main
f4f17194e04bcf8af65e8b5ff7aed7926b51ca88and CP #1204. Three preserved old checkout heads were clean, with no nonignored untracked source, and already ancestors of this published head. Source archives, patches, refs, custody metadata and hashes were retained before integration; old checkouts remain unchanged. No archived session was revived. The isolated candidate still has unresolved source conflicts and is not published or qualified.The complete old100-path inventory and updated24-path Pi overlap are recorded in the source map and per-path ledger. Standalone portable cleanup qualification is published in #1204: 14 tests / 71 assertions, with the original historical tests preserved. This does not qualify old native recovery or Railway restore.
Two concrete semantic conflicts: the old Pi restart delta changes unknown/zero-send to completed/one-send and drops dispatch-replay rejection evidence; preserve merged #1200 semantics. Old pending committed-cancellation intent delivery runs before fresh authorization and can deliver after revocation/expiry; it cannot replace the current revocable authority/unknown contracts. A test coordinator or two reads do not supply native commit-time authorization. Unique supported recovery work still needs integration/tests, with shared CI/database path ownership respected. Legacy LangGraph/Restate proof is historical, not the supported Railway Pi replacement.
No deployment/retrigger, credentials, grants, force-push, auto-merge or issue closure is authorized or performed. Refs adea-ai/adea#1173, #1025 and #942 remain open.
Preserved historical description and validation
Requested by Andrew · project thread
M17.02.2 — Profile recovery evidence and current validation receipt
Issue linkage: Refs #1025 (M17.02.2). Parent #942 remains open. This PR does not close either issue.
Status: Draft, auto-merge off. #1025 remains open. The milestone is not complete. This PR proves the clauses listed below at the named composition roots. The authoritative map is
docs/profile-recovery-acceptance-map.md.Historical validation head —
b145de3697e8ae41d3bc7935bd01c7eb534ac959Tree
7110d03ff387fb12e7b8e59054ad8e5d473ceacd. Base: mainf96ff8fb934f3ebfccdd72c749698b775ad7beb1(#1160, after release 1.100.5), contained in this head. One documentation-only commit over2d4f6f19, fast-forward, no history rewritten.Acceptance map reconciled with this receipt (review of
2d4f6f19).docs/profile-recovery-acceptance-map.md(the "Revalidate target generations" row, the per-profile "Target-generation fencing" cell, and gap 7) no longer says channel-generation fencing is proven against disposable PostgreSQL. They now say the test is not qualified at the current head: it needs PostgreSQL 17 or newer (transaction_timeout), it failed on local PostgreSQL 16, and the pinned PostgreSQL 18.6 lane has not run. The only passing evidence cited is the recorded full integration run on head9cd9bd48(2026-10-12, 316 passed; that head contains the test), labelled as a historical receipt and not a result for this head.Code at
2d4f6f19is unchanged, so its qualification below carries over; only the acceptance map differs. After the doc edit: format check (1,896 files), architecture audit,tests/repository.test.mjs(31) and the two tests that read the map (7) pass.Previous head —
2d4f6f1987243b5c77092e04109b7fc4daf7ebf5(code identical to the current head)Tree
f53d7bfbfce13749db7673a57496305d1f83cf09. Two commits overd4973c92, both fast-forward: the merge of main, and a fix so the merged result builds.Merge of main (#1134, #1156, #1160 and releases). Four Pi Durable files conflicted; both lines are kept:
packages/pi-durable-adapter/src/adapter.ts: main's widerturnKeysignature (used by its new completed-turn usage helpers) is kept. This branch's resume path no longer calls the owner-contention helper, so the helper is not carried.apps/control-api/src/pi-durable/node-admission.ts: this branch's fence-pin split is kept, and the newchildrenoperation is added to its operation types.apps/control-api/src/pi-durable/node-composition.ts: this branch's tracked public recoveries onrecoverandcloseare kept, and the child terminal settlement is added to recovery and to close.apps/control-api/src/pi-durable/pi-durable-lead.service.ts: this branch's admission path and retained-fence lookup are kept. The newchildrenread is rebuilt on that lookup: it refuses under any fence, and uses the start request for the observed target.node-admission.test.mjs: main's children-under-fence test now supplies the fence adapter that fenced status observation needs on this branch.The merge commit alone does not build (one unused helper left from the conflict); the head does.
Qualification of the code (Linux, pinned Node 24.21.0 and Bun 1.4.2 side-installed in the container; existing infrastructure only):
bun run buildbun run type-checkbun run lint, architecture auditbun run test:smoke/test:e2ebun run test:group:unitas rootapps/control-api/src/pi-durableteststests/profile-recovery-self-hosted-simple-sigkill.integration.test.mjsrestate-serverleft.Build, type-check, smoke, e2e and the SIGKILL runs executed one test-file line before
2d4f6f19(the addedfence: adapterinnode-admission.test.mjs); format, lint, architecture, the unit group and the Pi Durable directory ran on2d4f6f19itself. The non-root unit run and the PostgreSQL 16 integration run below were done ond4973c92, before the merge, and were not repeated.What did not run, and why:
docker composelane, the Docker disruption and restore drills, and the cloud remote drill did not run on this head.docker versionshows the client (29.8.2) anddocker composev5.6.0, but there is no daemon and no/var/run/docker.sock;docker compose up -d --wait postgresfails withfailed to connect to the docker API at unix:///var/run/docker.sock; check if the path is correct and if the daemon is running. NoDOCKER_HOST, remote database or Neon credentials are configured in this session. Starting a Docker daemon was refused by the session's permission policy earlier and was not retried; no access was changed.d4973c92, not this head): the repository's integration shards (50 files) against a disposable non-root PostgreSQL 16.15 passed 393 and failed 3, all inpackages/database/src/integration.test.mjs, each onset_config('transaction_timeout', …)inruntime-inventory-unit-of-work.ts. That is a PostgreSQL 17 setting and is absent on 16. One of the three is the channel-fencing test this PR adds, so it is unproven, not passing. The cloud remote drill timed out atinventory-ingestedon the same unit of work (cause not confirmed). The restore integration file passed 4/4 on 16. Not claimed as the pinned lane.Linux pidfds remain required; unsupported platforms fail closed. This is legacy LangGraph composition evidence with manual orphan reap, not hosted production recovery. Unexpected Restate launcher death before complete parent capture, product orphan adoption, Restate drain, upgrade/rollback, Hosted live recovery and production authority/reconciliation evidence remain unqualified. Refs #1025 and #942; both remain open. Refs #612 for the integration handoff; its remaining scope stays open.
Correction (earlier description): it said main
e688eb9awas contained. That commit is contained but was not current main; the real main at that time was2a02db89and thenf96ff8fb.2d4f6f19was the first head that containsf96ff8fb.Changes in the line since
59fe25fd(review of 2026-10-16)afterEachand the SIGINT/SIGTERM/SIGHUP path share one chained cleanup runner (tests/fixtures/runner-cleanup.mjs). A signal that arrives whileafterEachis awaiting a callback waits for it instead of seeing an empty queue and exiting. Callbacks are taken one at a time, so one registered during cleanup still runs. Cleanup stays handle-only with no force-kill fallback. Deterministic held-cleanup regressions deliver the signal during an in-flight cleanup; the first fails against the previoussplice(0)implementation.d4973c92). A child process (tests/fixtures/runner-cleanup-signal.child.mjs) installs the same shared runner and handlers and holds a cleanup callback. Real SIGINT, SIGTERM and SIGHUP must not end it mid-cleanup and must end it with 130, 143 and 129 afterwards; a signal with nothing in flight still cleans up. Seven tests; the SIGKILL file is 55 tests.initialuntil its 120 s keep-alive andrecoverindefinitely. Both now close on stdin end. Two regressions fail on the old fixture.graphWorkflowInput). A missing plan, a non-graph plan and an input that already has a graph pass through unchanged; a plan read failure fails closed. Applies only when the opt-in wake is configured. The same omission exists on the non-wake Hosted path and the Local reconciliation resume; not changed here.cancel()derives its target from the journal after authorization, not from a record read before it. Reproduced first: a generation that started during authorization left the cancelsupersededand the runrunning. Regression added.resume-permit.ts, not exported from the package entry). A structurally matching literal, a frozen literal and a cloned permit are rejected withPI_INFERENCE_RESUME_PERMIT_INVALID.fb3a2937…, base405a21e8) is merged with the remote cleanup line; the remote handle-owner design stands, withunconfirmedHandleson the teardown error and the shellkill $varscan kept.Historical validation receipts — tied to their original heads
Historical head —
d4973c920b6ba597b04292e5603a8b75971bb59a(superseded by the merge above)Tree
c7ac5b260239b99afe381a37a3681be72db65b89, fast-forward overd119fb39; tests, a fixture and the acceptance map only. Pinned Node 24.21.0 / Bun 1.4.2: forced build 45/45; format (1,884 files), lint, architecture audit and type-check passed; smoke 538 and e2e 408; unit 4,003 passed with the two root-only chmod failures and 4,014 passed, 0 failed as a non-root user (uid 1000, clean clone) including both permission tests; SIGKILL file 55/55 three times; the real-process signal proof passed on every run. Integration shards on a disposable PostgreSQL 16.15: 393 passed, 3 failed on the missingtransaction_timeoutsetting (details above). The pinned PostgreSQL 18.6 lane and Docker drills did not run.Historical head —
d119fb39c71442eac61eb104ac9d9c08ce659879(condensed)Tree
7ba3508a45ffebec278011657fd70d38df0a129c, fast-forward over59fe25fd. Linux, Bun 1.4.2 and container Node 22: build 45/45; format, lint and architecture clean; SIGKILL file 48/48; smoke 538, e2e 408; unit 4,003 passed with the 2 root-only chmod failures; adapter package 261/261; Hosted wiring and index tests 34/34.test:integration, PostgreSQL restore andtype-checkwere not run there.Historical head —
43ad930b7334eb925c8f8421b2a863b44295e9d7(superseded; not results for the current head)Tree:
9cad270fe034a1b58d39832c449af4c1204fe2ee. Repair base:3b59d23991dcce1d3e9625f68b03e971cdc115d9. This preserves the existing partial-start/stop-failure regression and adds the main-test cleanup repair. Changes are limited to the SIGKILL test, its fixture, and the acceptance map.The main test previously sent SIGKILL before recording Restate ownership after a startup timeout or partial identity capture. A Linux regression reproduced a live native Restate child after that cleanup. Teardown now requests a fresh ownership handshake from the live fixture, waits for startup to settle (including rejection), and pins the complete launcher/native chain before SIGKILL and child-first reap. The fixture retains the original launched process handle and keeps its control pipe alive until that handle's exit promise settles, including stop failures that clear the runtime health PID. A failed handshake preserves the live owner, partial handles, and work directory without a force-kill fallback.
New regressions exercise the same
releaseOwnershippath as the main recovery test: a pending-start barrier, startup timeout, failure between launcher and native capture, rejected startup with an injected stop failure, a provider stop timeout after the runtime clears its process reference, and failed ownership repair retaining live handles without signalling. Independent observer handles make the runtime regressions safe when run against the defective cleanup.Validation on that head, using Node 24.21.0 / Bun 1.4.2: SIGKILL plus self-hosted-simple integration files 46 passed, 0 failed (335 assertions);
bun run build45/45;bun run format:check,bun run lintandbun run type-checkpassed. Full repository unit/E2E/smoke groups and the complete PostgreSQL integration lane were not rerun for that repair.Historical head —
405a21e838febb13f4f639e9bf1029a16242d7a0Tree:
3d1bbd5ad89759d83a5f54859bf44ee0888532df. Parent:7657120a78099ccfc0753e9f5e0517863388d790(tree23cd8b64), which merged maindbd94385(release 1.97.1). Onlytests/profile-recovery-self-hosted-simple-sigkill.integration.test.mjschanged; no product source and no fixture changed.Owner teardown on pinned handles only (review of
7657120a)Before: the real-owner test's cleanup called
process.kill(childPid, 'SIGKILL')unconditionally, even after that child had been terminated and its pinned handle closed. If the pid had been reused, teardown could have killed an unrelated process.After: teardown is one helper,
releaseSpawnedOwner, that works only through handles: the owner'sChildProcess(SIGTERM, then SIGKILL after the bound) and the pinned child and anchor, each terminated through its handle if still running and then closed. Every step is attempted after an earlier failure, and a handle that cannot be confirmed gone is reported in anAggregateError. The numeric-pid kill is gone from the test file and no test or fixture callsprocess.kill.Local validation at that head (pinned Bun 1.4.2; container Node 22):
bun run format:check,bun run lint,bun run architecture:checkpassed, andbun teston the SIGKILL, repository and integration-shard files 64 passed, 0 failed. The build, unit, E2E and smoke lanes were not rerun for that one-test-file commit; their last results were at7657120a(build 45/45, unit 3,908 passed / 2 failed, E2E 403, smoke 521), where the two unit failures were the root-only chmod tests.type-checkand the fulltest:integrationlane were not run and are not claimed.Earlier heads, each with its own recorded results:
7657120a(launcher ownership anchored to the spawned composition; SIGKILL file 23/23),99c645f7(kernel start ticks required; SIGKILL file 17/17),1d759c8b(pinned pidfd handles for orphan termination; SIGKILL file 13/13),4fd4e090(resume-identity repair splittingresumeGenerationKeyandsafeResumeInferenceKey; adapter package 259 passed, E2E 397, unit 3,827 passed / 2 failed).Reviewed code receipt — exact code head
eb4b1a802f241f21f78aeec31e54af15130eea11Tree:
364dad8250b7ebf343eecc1b1ac8f4fd4158b196. Main/merge base:d6321979e44245e73a2a7ba3c4a913ddff149789. The reviewed code commit adds only the reviewed sibling-scenario fixture repair; it does not implement production cancellation authority. The fixture uses the existing test-only SQLite committed-cancellation coordinator, with its binding derived from retained admission and canonical execution/attempt records. Reviewed-tree results: the concurrent-siblings test 1 passed (59 assertions);bun run test:e2e396 passed, 0 failed; the missing committed-authority refusal test passed. A prior head ranbun run test:unit(3,783 passed, 11 skipped, 0 failed; 83.42% lines / 85.21% functions) andbun run test:smoke(521 passed, 2 skipped). Those prior-head results are not relabeled as reruns at later heads. The Hosted shutdown repair at7f22f15chas 18 lifecycle tests / 147 assertions and 45 scoped Hosted tests / 211 assertions; they are controlled-port lifecycle proofs, not live PostgreSQL/Restate recovery.Remaining production acceptance gaps
The authoritative acceptance map and architecture
partially_verifiedassessments retain these gaps. #1025/#942 remain open. Recorded checks at reviewed code head eb4b1a8: 21 contexts, 2 successful policy/draft checks and 19 skipped validations. Skips are not passing CI, and local results do not establish merge readiness.Prior periodic-scan test receipt — exact head
ae29515e8b0fed46eaaa0dc435b71b217eac7bd8(historical)Parent:
64bfc2ae5c045de559c9d3e8ef6261e541c42d66; tree:2f98adfff02e3e80ba7f8f61e544b186c03d0de6. This commit adds onlynode-composition-lifecycle.test.mjscoverage; no source implementation changed. The test holds the periodic scan at a promise barrier and verifies close waits, concurrent/repeated closes share completion, and no scan/journal access occurs after completion. It proves this composition teardown ordering with a controlled scan, not production authority, runtime task drain, provider cancellation, Hosted replay or global shutdown guarantees. At that tree: lifecycle file 4 passed (21 assertions), related eight-file lead suite 84 passed, scoped Oxfmt/Oxlint and pre-commit passed.Reviewed architecture repair receipt — exact head
64bfc2ae5c045de559c9d3e8ef6261e541c42d66Code qualification head:
cdd534caa1054fe65533b7070c42f1063395088d; main and merge base:d6321979e44245e73a2a7ba3c4a913ddff149789. That reviewed commit contains only the two-file architecture documentation repair (complete patch SHA-2566278153719f8880901884c2ecc8572c4a241a7e14038cb9d5be7424b510da385): it records the exact Hosted composition digest and regenerates one whitespace-only Markdown row. At cdd534c, 295 focused tests passed across adapter journal/claim/recovery/cancellation, lead lifecycle/fencing, profile wake, Hosted reconciliation wiring, Local crash/restore, fenced rollback, Pi SIGKILL restart and Hosted boundary suites. These tests qualify the original code head and were not rerun on the documentation-only repair.Hosted wake, lifecycle and production proof boundaries
Current code has an opt-in
bindRetainedProfileWorkflowWakebinding inHostedServerControlPlaneComposition, used only with caller-provided placement and guards. Fresh wiring tests cover binding, startup-failure cleanup, failed scheduler-drain cleanup and draining an accepted unstarted reconciliation before clearing its binding. They use injected guards and fixture resources, not live PostgreSQL + Restate active-attempt SIGKILL/replay. Original-product-actor/current-grant, trusted-residency and independently-current-topology sources remain unqualified. The binder admits retained zero-attempt wakes only. Revalidation does not make separate authority stores and dispatcher effects atomic.The Pi restart proof uses the real production startup factory, disposable SQLite, SIGKILL and loopback requests: it adopts the same retained handle/attempt/execution without resend. Safe continuation relies on test-only exact-key no-send evidence; production reconciliation evidence remains unqualified. Default committed-cancellation owner remains absent and fails closed.
Prior merged-tree validation receipt — exact head
9cd9bd4852483027b2310a6303c99b870afc7ce9(2026-10-12; historical)The full
bun run test:integrationrun on that published two-parent merge tree completed with 316 passed, 0 skipped, 0 failed against runner-owned disposable PostgreSQL, which the runner removed afterwards. That head contains the channel-fencing test. Qualification limits: the Cloud remote drill seeded the approval response and scripted the node/cancellation waiter; native permission origination, active cancellation confirmation, usage settlement and live provider execution remain unverified. This is a recorded receipt, not a result for the current head, and I could not re-run it here.Published feature-head validation receipt — exact head
7369e2fbf233fdf18f54e75f02530be15e6a1cb2(2026-10-12)Recorded locally on that head before the current-main merge:
format:checkpassed (1,791 files);lintpassed;type-checkpassed;bun run testbuild 45/45, unit 3,660 pass / 11 skip, E2E 347 pass, smoke 501 pass / 2 skip; database integration 66 pass;test:integration299 pass / 3 skip (the three skips were Hosted Server graph over PostgreSQL/Restate cases, unverified and not counted). These counts are historical for that exact head.Changed-test inventory review (66 PR files, historical)
The 33 active changed
.test.mjsmodules were assigned to lanes (16 unit, 4 integration, 7 E2E, 6 smoke); all 13 changed fixture/helper files had a consumer; no active changed test used.onlyor an unconditional.skip. This was an inventory check, not assertion-for-assertion equivalence. The self-hosted simple SIGKILL test is the legacy LangGraph composition with a manual orphan reap; it does not prove Pi Durable orphan adoption.Evidence carried forward from the prior receipts
hosted-simple, pinned Restate 1.7.13): paused-approval close/reopen, filesystem checkpoint restore, portable restore, and SIGKILL of the composition while its Restate child runs, with a manual orphan reap.9cd9bd48receipt above.Not proven (owners in the map's gap list)
drain(): excluded from this map. No open issue names it; root to assign.Open Pi items, unchanged: product revocation that reaches admission as evidence drift fails closed and persists no marker, and whether it should persist is a pending policy decision. Child-continuation denials are no longer persisted because message matching was removed; per-site classification is the follow-up.
Current remote state: Draft PR #1048; auto-merge remains off. The current head and its qualification are stated above.
🤖 Generated with Claude Code
https://claude.ai/code/session_01736NpDk6FAncqnfJiSCvUt