STAGE 1 / MILESTONES 15–17
Harness M15–M17: safe continuation and an attested native builder
Restart-safe continuation, a kernel-backed task runner, and signed authorship evidence: Harness M15–M17 connect safe checkpoints to verifiable patches, with 724 offline tests and explicit live gates.
01 / FROM SAVED STATE TO ACCOUNTABLE WORK
Can the agent continue—and prove which changes it made?
M14 could reconstruct a saved request, but deliberately refused to resume uncertain interrupted work. M15–M17 move the boundary forward: continue from an explicitly safe checkpoint, run the native kernel behind the trusted task gate, and bind its output to verifiable authorship evidence.
The three milestones were delivered together in PR #16, merged on September 11, 2026 as cfe7a80. This note follows that pinned revision and the shared build chat, not later changes on main.
02 / M15 — SAFE MEANS BEFORE NEW INTENT
A stored checkpoint is not automatically permission to retry.
M15 adds a safe runtime checkpoint phase before the next model round. It captures the original session state, agent identity, tool definitions, and workspace snapshot. A model or summary checkpoint is different: it marks a region where execution may have begun, so it cannot authorize an automatic retry.
safe checkpoint is the last committed boundary
+ same run / session / turn / model
+ same tool definitions and workspace snapshot
+ expired previous owner, fresh replacement owner
+ matching checkpoint revision and durable cursor
= eligible for a fenced continuation
model intent or uncertain effect after that boundary
= interrupted / indeterminate; do not execute again automaticallyThe runtime validates executable capabilities before consuming the checkpoint. A changed tool definition or workspace snapshot rejects continuation. The continuation restores prior messages, usage, counters, call identities, grants, warnings, steering, and compaction state instead of accepting a replacement conversation from the caller.
This continues the original run and turn. It does not emit a second agent-start event, turn-start event, or initial user message. A later follow-up is still a separate concept: new work after a completed turn.
03 / M15 — ONE OWNER TAKES OVER
Make the ownership change part of the durable transaction.
SQLite and Postgres implement continuation under their storage transaction or row lock. Admission checks the expected owner metadata and checkpoint revision, requires an expired lease and a different owner, and requires the safe checkpoint cursor to be the current event tail.
runtime.continued is appended in the same transaction that replaces the expired owner and advances the checkpoint cursor. A competing recovery attempt loses that claim; the former owner can no longer append under its old identity.
If work is indeterminate, the recovery path records an interrupted outcome rather than guessing. It also avoids closing a competing owner’s healthy session merely because another claimant lost. Cancellation during safe recovery completes the original turn as canceled without requesting the model.
04 / M15 — TEST THE CRASH BOUNDARIES
The interesting test is what does not run twice.
The continuation suite walks both sides of the scenario’s event appends: before commit, and after commit but before acknowledgement. It also injects failure before and after the tool effect. Only a latest safe checkpoint is resumable; other outcomes must make no additional model request and leave the effect count unchanged.
A separate fixture kills an actual child process with SIGKILL after a committed tool round. A file counter has already advanced from zero to one. The parent reopens SQLite, continues with a replacement owner, and verifies the counter stays at one. There is one tool call and two model requests across the session—not a duplicated tool effect.
child: tool increments counter → counter = 1
child: commit safe boundary → SIGKILL
parent: reopen SQLite → claim continuation
parent: finish next model round
counter remains 1
one tool.call across the session
two model.request events across the session05 / M16 — THE KERNEL BECOMES A TASK AGENT
Keep branch control outside the model’s five tools.
The native TaskAgent path now sits behind the existing bootstrap and exit gate. The trusted CLI validates the manifest, selects the exact task branch, checks scope, invokes the builder, applies its returned patch, runs exit tests, and records a structured report.
createNativeTaskAgent() requires an immutable image digest and the Docker workspace selector. There is no local fallback. It copies tracked input and the manifest overlay into a temporary source tree, compiles manifest permissions, and uses a SQLite-backed event store.
The model still receives only the M11 development tools. It does not gain a sixth tool to create branches, stage files, commit, push, or merge. Those operations belong to trusted workflow code and human review.
Bootstrap defaults to the offline native path. Upstream Pi remains available through explicit legacy selection. The default native model is FakeModel; selecting a native image alone does not turn the command into a live-model developer. That limitation matters when interpreting “self-host runner.”
06 / M16 — ONE END-TO-END OFFLINE FIXTURE
Edit, test, restart, return a patch—without touching main.
The integration fixture begins with a temporary repository containing an old text file and a task manifest. FakeModel writes the new text, invokes a test command, asks for a diff, and finishes. The trusted exit test independently checks the resulting host file.
Assertions verify the task branch, unchanged main reference, one test execution inside the protocol fixture, four model requests, one continuation event, unique event IDs, and a passing run-report/v2. A manifest write rule set to ask is resolved by the trusted run’s explicit approval.
The deliberate native restart closes and reopens SQLite and creates a new runtime while retaining the workspace object in the integration process. Docker execution is an injected protocol fixture. The separate M15 SIGKILL test supplies real process-death evidence; these are distinct tests, not a claim that a real Docker-backed builder survived a complete process loss here.
The important delivered integration is that model work now feeds the existing gate and reviewable patch path. Live-provider behavior and a deployed image still need their own qualification.
08 / M17 — BIND THE EVIDENCE CHAIN
Record the inputs, runtime, workspace, events, and output together.
Entrypoint MinimalAgentRuntime.TaskAgent/v1
Builder revision content digest of selected platform TS sources + lockfile
Control input manifest digest and input base SHA
Execution model, image digest, workspace/session/run identities
Snapshots initial/generated workspace and pre/post-builder Git state
Output generated Git tree and canonical patch digest
Audit runtime event log and workspace lifecycle log digestsThe builder revision is a content digest of the selected TypeScript source trees and lockfile—not merely a branch name, and not a hash of every byte in the repository. Generated tree capture uses a separate Git index so the verifier does not stage the user’s working index. Canonical patches disable external diff and text-conversion behavior.
The report is sealed with Ed25519 after tests and final tree verification. The signing key belongs to the trusted gate and is not passed to the model or workspace. The implementation supports an explicit signing key; without one it generates a process-local authority.
A signature binds the report to a key. It does not independently establish that the key or builder is trustworthy. Portable verification must use a separately pinned public key, not accept the key embedded in the report as its own trust root.
09 / M17 — VERIFY THE CANDIDATE AND ACCEPTED PATCH
A valid report must still match the code being reviewed.
harness verify-native validates the report, trusted signature, retained artifact digests, candidate tree, and canonical patch. If an accepted commit and its base are supplied, their patch must match the attested patch exactly.
harness verify-native tasks/runs/example-report.json \
--trusted-key trusted-builder-public.pem \
--candidate candidate-ref \
--accepted accepted-ref \
--accepted-base accepted-base-refThe tests cover a candidate matching the generated tree, a changed report, a tampered patch artifact, and a rebased or squashed patch on top of unrelated upstream work. The accepted tree can differ from the candidate tree because its base changed; the exact patch must not.
A human conflict edit breaks patch equivalence and is rejected with a direction to return that work to the builder. The successful verifier returns native-acceptance/v1 evidence linking candidate, accepted commit, base, trees, and patch.
This verifier exists as a read-only command. Passing PR CI for the M17 implementation is not the same as proving that this repository merge itself was generated by the attested native path. The candidate/accepted binding demonstrated here belongs to the independent temporary-repository fixture.
10 / RESULTS AND EVIDENCE LANES
724 tests, with real and simulated boundaries identified.
The build chat reports 724 passing tests, typecheck, evaluations, and the exit gate. During this publication, typecheck, the full offline suite, and evaluations were reproduced on the pinned merge.
- Offline suite: 724 passed; ten live-Docker tests remain skipped.
- Continuation: deterministic append/effect failure injection, competing ownership, cancellation, and a real child-process SIGKILL with SQLite reopen.
- Native runner: FakeModel, a Docker protocol executor fixture, a temporary Git repository, and an independent exit test.
- Authorship: seeded-source rejection, signed report/artifact checks, candidate matching, patch-equivalent acceptance, and rejection of human conflict edits.
- Postgres: injected transaction contracts, not a live database trial.
PR CI and PR CodeQL passed. The exact merge also passed CI and CodeQL.
No paid model call, live Docker image qualification, live Postgres deployment, load benchmark, or security certification is claimed by these results. “Offline gate implemented” is the correct milestone status.
11 / FOLLOW THE IMPLEMENTATION
Read recovery first, then the gate, then the verifier.
- Runtime and checkpoint schema: safe phase, continuation validation, restored state.
- SessionEventStore and storage contract: transactional takeover and interrupted recovery.
- Continuation tests: append crash matrix, ownership races, cancellation, and SIGKILL.
- Native TaskAgent: five tools, isolated workspace, SQLite session, and returned evidence.
- Trusted gate: clean-input checks, patch application, tests, and sealing.
- Attestation and verifier, with independent native fixtures.
- Updated builder operating guide and pinned roadmap: native workflow and the remaining live gate.
12 / M18 MUST EARN THE NEXT CLAIM
A mechanism for proving authorship is not self-authorship.
The next planned milestone uses a real model through the attested native path to add harness doctor to the project’s own CLI. It needs its own clean task, branch, safe restart, evidence, independent verification, human review, and merge.
Only after that gate is accepted does the resulting M18 candidate become the first qualified successor builder. The M17 revision that performed the work does not silently qualify itself. That separation keeps the self-hosting claim tied to a concrete authored change.
M15–M17 supply the prerequisites: a narrow safe continuation point, a native task path inside the trusted gate, and an independently verifiable link from clean inputs to accepted output.
Read the M11–M14 foundation or the M15–M17 build chat for the decisions leading here.