Harness/dev documentation
Scope
These pages are for framework maintainers working on Agentplane internals, harness behavior, release mechanics, and developer-only command surfaces.
They are intentionally omitted from the public user sidebar and public docs index. Public user docs should describe day-to-day repository operation and link to generated user command reference only.
Entry points
- Design principles
- Architecture
- Project layout
- Module topology
- Canonical Task cutover
- CLI contract
- CLI help JSON
- Workflow contract
- Workflow harness test matrix
- Harness engineering
- Local context implementation
- Agent Change Record implementation
- Close taxonomy
- Testing and quality
- Release and publishing
- Contributing
Canonical Task adapter
KernelBackendAdapter is the active boundary for the isolated taskKernel. The backend stores one
task_kernel extension containing the aggregate, ordered events, mutation receipts and record
digest in its existing atomic Task record. It does not introduce a second independently mutable
Task store. A backend must declare whole-record atomicity and revision-guarded writes. Cached
remote reads cannot qualify as canonical readback. The local implementation reuses the Task README
transaction. Cloud behavior is qualified against a CAS fake before it can claim canonical mutation
support.
Reads distinguish missing, canonical, archived, legacy-unmigrated and malformed records. Creation
calls the kernel with explicit intent and authority. Mutation never converts legacy status into a
canonical aggregate. A repeated command returns its existing receipt. A changed command with the
same mutation ID is rejected. Every write requires independent digest readback; a lost response
is reconciled by reading, and an unavailable readback remains write_in_doubt.
Local reads tolerate only a bounded transient identity change caused by the atomic README replacement that owns the canonical record. A persistent change, a symlink, or any other unsafe path remains fail-closed; retrying the observation does not synthesize a legacy aggregate or grant mutation authority.
task show renders canonical records through projectKernelTask. Archived records remain read
only. The old Task-centric adapter refuses records bearing the new extension, including malformed
ones. New tasks are canonical in 0.7.10. Legacy records require explicit migration and do not enter
the active route without a valid native identity.
Validation, evaluator and provider observation parsers bind evidence to the exact Task, plan, WorkItem, implementation, check, environment or effect request. They return observations, not completion decisions. Evaluator PASS is not deterministic verification. The application supervisor must submit an authorized command to the kernel before persisting a transition.
The adapter tests cover real local atomic writes, cloud-fake CAS equivalence, concurrent creation, response loss, missing readback, idempotency conflicts, malformed state and projection isolation. Migration backups, rollback receipts, the full replay corpus, and the 0.7.10 cutover have separate qualification gates; these adapter tests do not prove those gates by themselves.
On macOS, the Bun compilation smoke ad-hoc signs only its generated temporary executable before launch so an invalid linker signature cannot mask lifecycle startup failures.
Explicit Task migration
ap task kernel-migrate <task-id> performs a read-only dry run against the canonical local Task
README. It reports the raw source digest, classification, proposed canonical and projection
identities, expected output bytes, and rollback capability. It does not print source text or opaque
extension values. Unknown schemas, malformed records, ambiguous required inputs, and unreconciled
runtime leases or effects are quarantined. This command does not implicitly migrate Tasks used by
normal execution.
Apply the first Task as a canary with --apply --source-digest <dry-run-digest> --yes. Capture the
emitted JSON result as a repository-local proof file. The byte store uses the existing Task README
transaction owner. It saves an immutable content-addressed source copy, compares the source digest
and revision, and atomically writes the canonical record and migration receipt in that README.
An independent readback must match before the command reports success. A repeated apply with the
original source digest returns the same proof without changing bytes. A lost write response is
resolved by readback; unavailable readback is write_in_doubt, never assumed success.
ap task kernel-migrate <task-id> --rollback <proof.json> --yes restores the exact original bytes
only while the complete migrated output bytes, revision, receipt identity, and backup still match.
Later notes and metadata changes also block rollback. The batch adapter applies the first entry as
a canary and stops immediately on a refusal. The operator must not continue to later Tasks after an
unexplained canary mismatch. Backends without a proven raw-byte CAS and backup capability are
unsupported. Real repository corpus migration and legacy-route deletion remain behind the M3 gate.
Frozen replay and restart qualification
The kernel-replay*.corpus.json files preserve exact source bytes, digests, reference commit,
expected observations and a reproduction command. Tests replay these inputs without refreshing
their expectations. The explicit capture scripts refuse to overwrite an existing corpus. A
capture requires a committed reference whose relevant implementation paths match the source.
Comparison reports both digests and the first divergent field, including event or receipt order.
node scripts/bench/qualify-kernel-replay.mjs <exact-commit> prepares an isolated checkout through
the existing exact-anchor clone helper. It removes the remote, uses the anchored workspace source,
rejects source resolution outside that checkout, checks dependency identity before and after the
run, and verifies that tracked files remain unchanged. Its JSON result identifies the commit,
tree, driver, dependencies, corpus files and test report. Run it on the committed implementation;
a successful test in a dirty development checkout is not this qualification evidence.
Persistence replay covers direct, branch PR, context, release and batch model journeys through real local storage and an explicit cloud CAS fake. Interruption tests cover every atomic-write boundary in the journey, before write, after write and with unavailable readback. A new adapter instance must recover without applying a second mutation. Provider observations remain fakes; these tests do not constitute live publication, production self-hosting or release drills.
A WorkItem claim cannot overlap a resource held by another claimed, executing, inspecting, validating, blocked or uncertain WorkItem in the same Task. Idle, rework-ready and terminal items do not retain the claim. The aggregate CAS serializes competing claims; a retry must observe the new revision and resource owner. Cross-Task scheduling still requires the supervisor's resource ownership boundary. This kernel check does not replace that boundary.
The complete twelve-family corpus, comparison-only legacy/canonical read qualification, and exact-anchor report remain required M2 gates. M3 separately requires the production cutover, repository corpus migration, legacy deletion, full crash matrix, twenty self-hosted Tasks and three release drills. Do not infer those outcomes from the replay test count.
Framework checkout handoff
Inside the Agentplane framework repository, the installed agentplane wrapper delegates to the
repo-local development binary after bootstrap. Check the active runtime with:
agentplane runtime explain
For fresh clones, worktrees, or stale repo-local builds, use the canonical framework bootstrap:
bun run framework:dev:bootstrap
Set AGENTPLANE_USE_GLOBAL_IN_FRAMEWORK=1 only when you intentionally need to bypass the
repo-local development runtime.
If commands fail with refusing to run a stale repo build, rebuild with the bootstrap command,
then verify the repo-local handoff with:
node packages/agentplane/bin/agentplane.js runtime explain
agentplane runtime explain
Deterministic local subprocess runtime
packages/agentplane/src/shared/runtime-env.ts owns local executable discovery.
The runner and declared-check execution paths use the same environment normalization.
Recovery that resumes either path uses the same implementation.
Resolution order
- Explicit invocation/profile PATH entries.
- Explicit absolute
NVM_BIN,VOLTA_HOME/bin, andBUN_INSTALL/binlocations. - Inherited PATH entries in their original order.
- The highest executable semantic-version Node installation under
HOME/.nvm/versions/node. HOME/.bun/binand the running executable directory.
Normalization returns a copy. It does not mutate the parent environment or any authority.
Explicit executable arguments remain unchanged. No per-agent runtime path is required.
Empty manager variables never become implicit working-directory executable candidates.
Discovery rejects non-executable files and ignores relative PATH entries for identity resolution.
An explicit relative executable argument is resolved against the invocation working directory.
Windows executable discovery additionally uses PATHEXT.
When the CLI runs under Bun, the Node resolver never uses Bun as a Node fallback.
Missing Node returns an ENOENT infrastructure error.
Evidence and failure classification
The runner observes the executable content SHA-256 and a digest of runtime-selection inputs. The observed runtime is attached to the process-start event and to a required execution-receipt check. Receipts do not store environment values or use environment data as authorization. An unreadable executable identity remains unqualified. The prepared invocation snapshot also binds runtime identity. A changed inherited/profile PATH or executable content invalidates prepared input before another child can launch.
Declared checks store runtime identity with their recorded command and outcome.
A missing/inaccessible executable is infrastructure evidence and returns the existing
unsupported verification status instead of an implementation-failure verdict.
A real process failure with a resolved runtime remains an implementation check failure.
The shell-check helper exposes failure_kind: infrastructure for launch failures.
Qualification
Existing resolver and direct-verification tests cover override precedence, numeric NVM ordering,
non-executable candidates, missing commands, parent-environment preservation and typed evidence.
packages/agentplane/src/runner/runtime-env.integration.test.ts launches actual child processes
with a fixture HOME and reduced PATH. Its executable shim verifies lookup independently of a
machine-specific Bun installation. Standalone and root-referenced invocation identities share
resolution; a profile PATH wins. The test also writes and reads the execution receipt and
checks a genuinely absent executable. The POSIX executable-shim test is skipped on Windows;
portable resolver and receipt tests remain enabled.
Full local CI, evaluator acceptance, exact-head hosted checks and hosted closure remain required. This change does not install a runtime, change remote/container execution, or publish 0.7.8.
Runtime evidence also fingerprints the selected Node and Bun executable contents behind shell or script launchers. Replacing either runtime under an unchanged PATH invalidates prepared input even when the outer runner script is unchanged. Runtime digests are computed from bytes for each observation; file metadata alone is not a freshness proof. The Node selector reads the normalized PATH first, so an explicit profile path wins over inherited manager locations.
Canonical Task cutover
Scope and current boundary
Task 202608291006-255K66 implements M3 from the
rebuild specification.
Its approved planning baseline is 36741ce5160d452ca9660a388241cb4da32f842a.
The first WorkItem changes read projections only. It does not migrate records, transfer the
controller, enable canonical writes, dispatch effects, or satisfy final M3 acceptance.
Read contract
commands/task/kernel-read.ts is the shared command boundary for canonical status, brief,
next-action, readiness, active selection, and show. It reuses readKernelRecord,
projectKernelTask, and readKernelNextAction.
| Stored input | Read behavior |
|---|---|
| Canonical record | Validate repository identity and record invariants; derive state and next action from the kernel. |
| Archived record | Return an explicit read-only archive view; exclude it from active work. |
| Malformed canonical extension | Report invalid canonical data; never fall back to legacy status. |
| Missing record | Report a missing read; do not invent a Task aggregate. |
| Unmigrated legacy record | Keep the existing legacy inspection route until explicit migration. |
| Inaccessible backend | Propagate the error; do not classify it as missing. |
Canonical status, brief, and next-action JSON expose source, record_kind, task,
ready, next_action, and read-only authority. These commands do not prepare a legacy
WorkOrder. show retains its existing canonical output shape.
Readiness means that the kernel selects a claimable WorkItem. Planning and blocked Tasks are not
reported ready merely because the legacy dependency list is empty.
Active selection reads canonical state before filtering legacy status. Its
dependency_readiness.state=kernel_work_items distinguishes the canonical WorkItem graph from
legacy inter-Task dependencies. It does not reinterpret one graph as the other.
No read projection grants authority or supplies an executable mutation command.
Without a current implementation fingerprint, final-validation routing returns
kernel_repository_fingerprint_required. It cannot claim completion from stale validation.
Logical repository identity inspection cannot create identity files in an unborn repository.
The existing ready missing-task contract is preserved: it prints the unknown-task warning and
returns exit code 2. The typed missing read does not create a synthetic aggregate.
Production owner map
Paths below are relative to packages/agentplane/src, except the pure core.
| Responsibility | Current owner | Required cutover owner |
|---|---|---|
| Pure transitions and invariants | packages/core/src/tasks/task-kernel | Same kernel; no platform imports. |
| Durable record and atomic mutation | adapters/task-backend/kernel-record.ts, kernel-backend-adapter.ts | One canonical record and mutation receipt. |
| Command read projections | commands/task/kernel-read.ts | Shared validated kernel read with bounded atomic-replacement observation. |
| Synthetic runtime and aggregate | adapters/task-backend/task-centric-backend-runtime.ts | Remove mutable runtimeFrom/aggregateFrom legacy construction after controller transfer. |
| Plan and approval | commands/task/plan.ts, plan-shared.ts, plan-approve.command.ts | Canonical plan commands and authority comparison. |
| WorkItem result application | commands/task/task-centric-external-result.ts, external-agent-result-application.ts | One canonical result admission service. |
| Route and WorkOrder preparation | commands/shared/route-decision.ts, runner/usecases/agent-work-order.ts | Kernel next action plus separately admitted effects. |
| Verification and completion | commands/task/verify-record-execute.ts, finish-execute.ts, shared/transitions.ts | Kernel validation identity and completion predicates. |
| CLI and managed controller | commands/task/advance.command.ts, external-agent-supervisor.ts, branch-task-supervisor.ts, direct-task-supervisor.ts | Shared canonical application service. |
| External effects | adapters/task-backend/kernel-effect-dispatch.ts, kernel-observations.ts | Intent, dispatch, readback and reconciliation with exact identity. |
| Migration and replay | adapters/task-backend/kernel-migration.ts, kernel-replay.ts | Explicit receipt-bound migration with byte-exact rollback. |
| Remaining legacy display consumers | Task list, search, export and formatting consumers | Inspect separately during retirement; retain only non-authoritative compatibility projections. |
The planning-base recovery helper landed after the frozen M3 planning baseline in PR #5883. Its legacy runtime inspection must also be considered when the final integrated import inventory is computed. A frozen worktree inventory alone is insufficient retirement evidence.
Ordered gates and rollback
| Order | WorkItem | Gate before proceeding | Rollback boundary |
|---|---|---|---|
| 1 | m3-projections | Canonical reads ignore contradictory legacy fields; missing, archive and corruption cases are explicit. | Revert read wiring only; no stored bytes changed. |
| 2 | m3-lifecycle | Creation, plan, approval, claims and results pass the same local/cloud fake contract. | Stop new canonical mutation; restore the recorded controller/state checkpoint. Never synthesize legacy authority. |
| 3 | m3-effects-validation | Final validation, evaluator decisions and evidence projection use canonical identities and reject stale proof. | Restore the last accepted evidence checkpoint; do not dispatch repository or provider effects. |
| 4 | m3-effects | Git, PR, merge, hosted close and cleanup use admitted effects and observed receipts. | Reconcile uncertain effects before any retry or rollback. |
| 5 | m3-task-classes | Direct, branch PR, context, release and batch use the same service. | Restore the class checkpoint only after pending effects are resolved. |
| 6 | m3-crash-migration | Every specified crash boundary passes; real repository corpus migration has receipts. | Restore exact source bytes from verified migration backups. |
| 7 | m3-self-hosting | Twenty real sequential Tasks complete without manual state edits or duplicated effects. | Use the recorded controller-transfer receipt and reconcile in-flight work. |
| 8 | m3-retirement | Production import audit finds no prohibited mutable legacy authority. | Revert code only against the corresponding compatible state checkpoint. |
| 9 | m3-final-qualification | Full CI, self-hosting evidence, clean status, and hosted exact-head checks agree on identities. | Keep delivery blocked on any missing or stale proof. |
Each later gate must record its own concrete checkpoint, evidence digests and restore readback. This table defines the required rollback boundaries; it is not a claim that those receipts exist. When a plan refinement changes only future gates, a completed WorkItem with a semantically unchanged definition must retain its runtime and evidence provenance. New or changed future WorkItems must be materialized from the approved revision. Ambiguous prior-plan history must fail closed instead of resetting completed work or replaying implementation. Release drills and stable release publication are outside this Task's approval and remain in the separate release lane. They are not substitutes for the M3 self-hosting or hosted exact-head gates.
Projection-stage verification
The focused command and identity suites passed 15 tests across five files, including a real CLI readback that preserves exact Task README bytes. Typecheck, changed-source ESLint, architecture checks, documentation IA, and all eight lifecycle invariants passed. The first full CI attempt exposed a forbidden test-layer import and the missing-task exit-code regression. Both were corrected without changing the guards or assertions. The 30-test control run covers the source-layering guard, existing workflow behavior, canonical reads, and real CLI readback.
The broader run-cli.core.task-next-action-json.test.ts probe has one existing failing case:
stores branch_pr authority without changing the task branch before the authorized PR operation.
It expects approval.pr.open but receives agent.branch_implementation. The same failure was
reproduced on unmodified main at e16259bf9666e02c2099df5c5b21c43d8e90c1ca.
This is not a passing verification result. The lifecycle stage must reconcile the fixture with real
WorkItem completion before final M3 qualification. No required check or expected assertion was
removed or weakened.
Recover an unstarted planning-base worktree
A queued Task can retain a creation commit older than its approved planning snapshot. If native workspace preparation then reports already-completed dependencies as missing, inspect both commit identities before changing any dependency.
agentplane work resume <task-id> --refresh-planning-base --json
agentplane work resume <task-id> --refresh-planning-base --apply --expect-token <sha256:digest>
The first command only inspects. The second consumes the exact inspection token. Recovery accepts only a local, uniquely owned, unstarted Task worktree with an approved structured plan, an implicit creation-checkout base, no runner/provider history or active claims, and no source changes. The approved snapshot must descend from the original base and remain on the development branch. The transported Task README cannot collide with a tracked path in the target snapshot.
AgentPlane holds the native Task revision lock, fast-forwards Git, confirms the new HEAD, and records
the recovered execution base. It preserves the plan, dependency list and WorkItems. If interrupted
after Git advancement but before Task publication, inspect again and apply the fresh token to
reconcile the recorded base without repeating the Git effect. After a process crash, recovery validates
any orphaned native publication candidate against the same Task revision, plan, repository and Git
identities. It archives the exact bytes under the common Git directory at
agentplane/planning-base-recovery/<task-id>/ before removing that candidate from the worktree.
Recovery accepts up to eight verified orphan candidates and reserves one additional slot for the
current native publication. Unknown or changed candidates remain untouched and block recovery. Explicit base pins, divergent history,
started work and unknown artifacts fail closed. The command does not fetch, publish, weaken checks,
or start a runner. After recovery, bootstrap the framework runtime if its source changed, then request
a fresh task advance --agent-json packet.