akm docs

P0 — invariant inventory for the task/workflow refactor

Status: ready for test authoring Phase: P0 of the akm task/workflow refactor Owner artifacts: tests only — P0 changes no production code

This document is the single source of truth for the three P0 test-writing lanes. Lanes do not re-derive these facts from the codebase and do not read the parent plan. Every file:line reference below was verified at the current head of claude/breaking-changes-0-9-2-3cfyvp (== release/0.9.2).


0. What P0 is

P0 pins current behavior — including behavior we have already decided is wrong — with passing characterization tests. Nothing is fixed here.

The contract that makes the later phases cheap:

Therefore:

ID scheme

P-nn = preserve. R-nn = replace. Lanes cite these IDs in test names so the phase that flips a row can find its test with a grep.


Behavior to preserve

These are load-bearing today and stay load-bearing after the refactor.

# Behavior Evidence (file:line) Observable surface to pin Lane
P-01 Task-layer with on a command ref is rejected loudly. The task layer, unlike the workflow layer (R-01), never silently drops with. src/tasks/runtime-v3.ts:397-401 UsageError, code INVALID_FLAG_VALUE, message exactly:
Task v3 command refs do not accept with; use akm/command with {ref, arguments} for portable arguments.
B
P-02 Task-layer with on a script ref is rejected loudly. src/tasks/runtime-v3.ts:437-439 UsageError, code INVALID_FLAG_VALUE, message exactly:
Task v3 script refs do not accept with.
B
P-03 Task-layer with on a workflow ref is accepted and becomes the child workflow's params. This is the one with consumer that works, and it is the shape P2b generalizes to workflow steps. src/tasks/runtime-v3.ts:432 — params: Object.freeze({ ...(document.target.with ?? {}) }) prepareTaskV3Execution returns { kind: "workflow", ref, params } where params deep-equals the authored with mapping; absent with yields {} (not undefined); the object is frozen. B
P-04 A workflow-target task with any env: is rejected. Adjacent to P-03 and read at the same site during spec authoring; lane B will hit it while building P-03 fixtures, so it is pinned rather than left as a surprise. src/tasks/runtime-v3.ts:415-421 UsageError, code INVALID_FLAG_VALUE, message exactly:
Task v3 workflow env cannot be consumed by the durable workflow runtime in 0.9.2; remove env or use a command target.
Triggered when Object.keys(environment).length > 0.
B
P-05 Task-runner provenance, workflow arm: AKM_EVENT_SOURCE is stamped on the global process.env for the duration of the run, then restored. This arm executes in-process, so child akm invocations made by workflow steps inherit the stamp. A more specific pre-existing value wins. src/tasks/runner.ts:534-535 (stamp), :552-555 (restore in finally) During the run, process.env.AKM_EVENT_SOURCE === "task" when it was previously unset; it is deleted again afterward when it was previously undefined, and restored to the prior string otherwise — including on the throwing path (finally). A pre-set AKM_EVENT_SOURCE=improve survives untouched. B
P-06 Task-runner provenance, native shell/script arm: AKM_EVENT_SOURCE is passed in the child env only — process.env is never mutated. src/tasks/runner.ts:389-393 — AKM_EVENT_SOURCE: process.env.AKM_EVENT_SOURCE ?? "task" inside the runManagedSubprocess env bag The env handed to the spawn contains AKM_EVENT_SOURCE="task" (or the inherited more-specific value); the parent's process.env.AKM_EVENT_SOURCE is unchanged before, during, and after. B
P-07 resolveUsageEventSource defaults an unset/empty AKM_EVENT_SOURCE to "user", and maps an unrecognized value to "unknown" rather than to "user". This default is what turns R-07 into a real defect; pin it so R-07's flip is unambiguous. src/indexer/usage/usage-events.ts:28-32 undefined/"" → "user"; each of user/improve/task/audit/unknown → itself; anything else → "unknown". B
P-08 Multi-job YAML parsing is well-formed and canonical: 1–256 jobs, needs validated, jobs emitted in one canonical topological order. R-05 replaces only the execution-time rejection; the parser's job model is the thing the P4 adapter boundary will be built on, so its behavior must not drift in the meantime. src/workflows/source-ir/github-yaml.ts:464-536; src/workflows/source-ir/ordering.ts 0 jobs or >256 jobs → failure code job-count-limit, message workflow.jobs must contain 1 through 256 jobs.
Unknown needs target → missing-job-dependency, message Job <id> needs missing job <dep>.
Cycle → job-dependency-cycle, message Workflow jobs contain a dependency cycle.
Duplicate needs entries → duplicate-job-dependency. needs is sorted. Ordering: emit the lexically-first ready job, then recompute readiness (canonicalTopologicalJobs) — pin the exact emitted order for a fixture with two independently-ready jobs.
A

Behavior to replace

Each row is a deliberate future change. P0 pins it passing; the named phase flips exactly the pinned tests.

# Behavior (current, wrong) Evidence (file:line) Observable surface to pin Flips in Lane
R-01 A workflow step's with: on uses: tasks/<ref> is accepted by the decoder and then silently DROPPED. The decoder shape-checks with for every uses step but only the builtin-command target ever validates or consumes it; taskDispatch reads source.with nowhere. The authored mapping vanishes with no error, no warning, and no trace in the frozen plan. Accepted: src/workflows/source-ir/schema.ts:144 (field), :389-393 (shape-check only), :371 (target validation reached only for builtin-command).
Dropped: src/workflows/ir/source-freeze-v4.ts:211-272 (taskDispatch — no source.with read); the sole consumption of source.with is the builtin-command branch at :148.
(a) A step uses: tasks/x with with: {a: 1} decodes without error.
(b) scalarRecord(step.with, …, true) still rejects a non-scalar with value and :393 still fails a with without uses (step <id> with is legal only with uses).
(c) Freezing that step produces a resolved dispatch byte-identical to the same step with no with: at all — assert equality of the two frozen results, which is what makes the drop visible.
P1a rejects it; P2b implements real bindings A
R-02 directScript fabricates synthetic task YAML and re-parses it. A uses: scripts/<ref> workflow step is executed by writing a task document as a string — including a fake @daily schedule that exists only to satisfy R-06 — and running it back through parseTaskV3Yaml. src/workflows/ir/source-freeze-v4.ts:274-298 The synthetic document is exactly:
version: 3\nuses: <owned.ref>\nakm:\n schedule: "@daily"\n
with filePath = \${context.asset.path}#${source.id}`, workspaceRoot = context.asset.sourcePath, taskId = source.id, taskRef = `${context.asset.ref}#${source.id}`. These identity fields are computed by directScript()but are **not observable** anywhere production surfaces:scriptResult()reads only.sourceRef/.interpreter/.extension/.bytesBase64/.byteLength/.sha256/.cwdIdentityoff the prepared execution intoFrozenWorkflowScriptTarget— never.taskId/.taskRef — and no persisted plan carries them. Pin **only** the observable surface: a script step freezes to a script dispatch carrying the script's own qualified ref (FrozenWorkflowScriptTarget.ref), its exact bytes, and its interpreter. The bare invariant at :296 (if (prepared.kind !== "script") throw new Error("direct script did not project as a script"), exit 70, not a UsageError) is unreachable from directScript`'s own call site — see the Review log — and is not pinned.
P1b (typed preparer) A
R-03 Nested-workflow targets are rejected at three call sites in source: two reachable independently, and one a documented-dead duplicate of the second. src/workflows/source-ir/semantics.ts:141-146; src/workflows/ir/source-freeze-v4.ts:220-222; src/workflows/ir/source-freeze-v4.ts:237-239 (unreachable in practice — see the Review log) Pin the two independently reachable sites; record the third rather than pinning it.
semantics.ts: WorkflowSourceSemanticError, code nested-workflow-unsupported, message Nested workflow target "<value>" is unsupported in a workflow step. (value JSON-stringified).
source-freeze-v4.ts:220-222: UsageError, code INVALID_FLAG_VALUE, message exactly A workflow task step cannot compose a nested workflow target., firing on the parsed task document (task.target.uses.kind === "workflow").
source-freeze-v4.ts:237-239 guards the byte-identical message one call later in the same taskDispatch, on the condition prepared.kind === "workflow" — but prepareTaskV3Execution returns kind: "workflow" only when document.target.uses.kind === "workflow" (runtime-v3.ts:415), the exact fact the :220 guard already tested on the same parsed task earlier in the same call. No fixture can pass :220 and still reach :237 with prepared.kind === "workflow"; it is a dead duplicate, not independently pinnable.
P3 (child workflows) A
R-04 GitHub Action locators are parsed into a full typed target, then rejected downstream. The grammar is live: owner/repo[/path]@rev classifies successfully with owner/repository/path/revision fields — and is then refused at both consumers. Parse: src/tasks/source-v3.ts:523 (classifyTaskV3Uses), :562-588 (locator branch).
Reject (task prepare): src/tasks/runtime-v3.ts:366-371.
Reject (workflow step): src/workflows/source-ir/semantics.ts:134-139.
(a) classifyTaskV3Uses("owner/repo@v1") returns a frozen { kind: "github-action", ref, owner, repository, revision }, and with a path owner/repo/sub/dir@v1 adds path: "sub/dir". Pin at least one accepted shape and one rejected locator falling through to the trailing throw: Task v3 uses must be akm/command, a canonical commands/, workflows/, or scripts/ asset ref, or owner/repo[/path]@ref. Agent/task/local/Docker/ambiguous targets are not executable.
(b) Preparing such a task throws UsageError / INVALID_FLAG_VALUE: GitHub action "<ref>" is recognized but remote action acquisition is unsupported in 0.9.2.
(c) The same locator in a workflow step throws WorkflowSourceSemanticError code remote-action-acquisition-out-of-scope: Remote action acquisition is out of scope for "<value>".
P4 (grammar removal) A + B
R-05 Multi-job YAML parses fully, then execution refuses it. Two independent rejection points on two paths, both after a complete parse. Parse: src/workflows/source-ir/github-yaml.ts:464-536 + src/workflows/source-ir/ordering.ts (see P-08).
Reject (freeze path): src/workflows/ir/source-freeze-v4.ts:105-110.
Reject (direct compile path): src/workflows/ir/compile.ts:117-127.
(a) A 2-job source parses clean and yields 2 ordered jobs (P-08 covers ordering).
(b) Freezing it throws UsageError, code INVALID_FLAG_VALUE, message exactly:
Multi-job workflow cannot execute until job boundaries and needs have a durable runtime representation.
(c) compileWorkflowPlan on the same IR returns (does not throw) { ok: false, errors: [{ line, message: "Current workflow execution requires exactly one source-IR job." }] }, with line from the second job's span. Pin the return-vs-throw asymmetry — it is easy to lose in P4.
P4 (adapter-boundary rejection) A
R-06 Task v3 requires exactly one scheduling source. Declaring neither akm.schedule nor on: is an error, and declaring both is the same error. This is what forces R-02's fake @daily. src/tasks/source-v3.ts:636-651 Both the neither-case and the both-case fail with the same source error text: must declare exactly one scheduling source: akm.schedule or on. (raised via sourceError(ctx, [], …) — pin the rendered message and the empty path).
Also pin the success shape of the akm.schedule arm: { manual: false, schedules: [{ cron: <schedule>, source: "akm.schedule", ordinal: 0 }] }, frozen.
P2a (optional schedule) B
R-07 Task-runner provenance, prompt arm: AKM_EVENT_SOURCE is NEVER set — anywhere. runPreparedCommandTask neither mutates process.env (as P-05 does) nor passes a child env (as P-06 does). Combined with P-07's "user" default, a scheduled prompt-target task records its nested akm usage as user demand. This is a DEFECT, pinned as-is. src/tasks/runner.ts:679-737 (whole arm — no AKM_EVENT_SOURCE occurrence); default at src/indexer/usage/usage-events.ts:28-32 Run a prompt-target task with AKM_EVENT_SOURCE unset. Assert: (a) process.env.AKM_EVENT_SOURCE is still undefined at every point of the run, including inside the injected runAgent/chatCompletion impl; (b) no AKM_EVENT_SOURCE reaches the dispatch; (c) consequently resolveUsageEventSource() observed from inside the arm returns "user". Point (c) is the row's whole reason to exist — write it explicitly so P1b's flip to "task" is a one-line test diff. P1b (defect fix) B
R-08 Task result vocabulary is inverted, and the inversion is persisted. A prepared command (agent/LLM) becomes result kind "prompt"; a prepared shell or script becomes result kind "command". Those exact strings are written to task_history.target_kind and read straight back out. Mapping: src/tasks/runner.ts:256-260 (preparedResultTarget) — :257 workflow, :258 command→prompt, :259 shell/script→command.
Persist: src/tasks/runner.ts:1081 — target_kind: result.target.kind === "unknown" ? null : result.target.kind.
Read back: src/tasks/runner.ts:1134-1158.
Per arm, pin the stored string and the round-trip:
• prepared workflow → target_kind "workflow", read back as { kind: "workflow", ref } (ref falls back to "" when the column is null).
• prepared command → target_kind "prompt", read back as { kind: "prompt", engine } (engine from metadata, null when absent).
• prepared shell → target_kind "command", read back as { kind: "command" }.
• prepared script → target_kind "command" (same string as shell — the two arms are indistinguishable in history).
• target_kind null / unrecognized → { kind: "unknown" } on read.
P1b (read-boundary mapping) B
R-09 Legacy "stash" naming survives at the task boundary. RunTaskOptions.stashDir is the option name for what is now a bundle root, and the bundle-name resolution ends in the literal "stash". src/tasks/runner.ts:111 (stashDir); src/tasks/runner.ts:173 — options.bundleName ?? config.defaultBundle ?? "stash" Pin observable effects only — do not pin the identifier name. With no options.bundleName and no config.defaultBundle, the task's fully-qualified ref is built against bundle stash (i.e. stash//tasks/<id>), and that is the bundle identity threaded into preparation. Pin the resulting ref string, not the option key: the option key is renamed in a later phase and a test asserting on it would fail for the wrong reason. P1b / P4b (boundary rename) B

Lane pin checklists

Three lanes work in parallel. Each lane owns its rows end to end; no row is shared between lanes except R-04, which is split explicitly below.

Lane A — workflows

Owns everything under src/workflows/.

Lane B — tasks

Owns everything under src/tasks/ plus the usage-events default.

Harness note for P-05 (and P-06's negative half): tests/_preload.ts installs a tripwire that throws if a test leaks an AKM_* env var that was not present at preload time (AGENTS.md, "Test-isolation harness"). P-05 exercises a production path that mutates process.env.AKM_EVENT_SOURCE on purpose. Observe the stamp from inside the run (via an injected runWorkflowStepsImpl) and let the production finally restore it; never leave the var set across the test boundary and never set it by hand outside the sandbox helpers.

Lane C — fixtures

Owns the shared fixture surface both other lanes build on. Lane C lands first; A and B consume it.


Acceptance criteria


Review log

2026-08-25 — R-03's third site (src/workflows/ir/source-freeze-v4.ts:237-239) is unreachable in practice. That guard fires on prepared.kind === "workflow", where prepared is the return of prepareTaskV3Execution. prepareTaskV3Execution (src/tasks/runtime-v3.ts:415) returns kind: "workflow" only when the parsed task document's target.uses.kind === "workflow" — exactly the condition the :220-222 guard in the same taskDispatch call already tests, on the same parsed task object, before prepareTaskV3Execution is ever invoked. No fixture can pass the :220 guard and still reach :237 with prepared.kind === "workflow": the two sites guard one condition twice, not two independently reachable failures. The R-03 row and the Lane A checklist have been amended to name two reachable sites (semantics.ts:141-146, source-freeze-v4.ts:220-222) plus this documented-dead duplicate, rather than requiring a third, unreachable pin. Left unfixed — no production change in P0.

2026-08-25 — R-02's bare invariant (src/workflows/ir/source-freeze-v4.ts:296, if (prepared.kind !== "script") throw new Error("direct script did not project as a script")) is unreachable from directScript's own call site. directScript is invoked only from resolveStep (source-freeze-v4.ts:144) when target.kind === "script", and the synthetic task document it fabricates always sets uses: <owned.ref> where owned.ref is built with plural = "scripts" (resolveOwnedAssetCore) — so the synthetic document's uses: always classifies, and always prepares, as kind: "script". No fixture reachable through directScript's own call site can make prepareTaskV3Execution return anything else. Exercising the :296 throw would require calling directScript (or prepareTaskV3Execution) with an argument shape it never receives in production. The R-02 row has been amended to not require pinning this invariant. Left unfixed — no production change in P0.

2026-08-25 — R-02's synthetic-document identity fields (filePath, taskId, taskRef) are computed but not observable anywhere production surfaces. directScript (source-freeze-v4.ts:274-298) fabricates filePath = \${asset.path}#${step.id}`, taskId = step.id, and taskRef = `${asset.ref}#${step.id}`, and threads them through prepareTaskV3Executionvia the synthetic document. ButscriptResult()— the only reader of that prepared execution — copies just.sourceRef/.interpreter/.extension/.bytesBase64/.byteLength/.sha256/.cwdIdentityintoFrozenWorkflowScriptTarget; .taskId/.taskRefare dropped. No seam at head re-exposes them either: the one other exported entry point over this path,resolveWorkflowSourceV4, returns the same FrozenWorkflowTargetshape. A characterization test can therefore only pin these fields by hand-constructing the same literalsdirectScriptcomputes and feeding them toparseTaskV3Yaml/prepareTaskV3Executiondirectly (astests/workflows/characterization-classification.test.ts's R-02 identity-contract test does) — which asserts the test's own inputs round-trip, not a surface directScript` itself could break. The R-02 row has been amended to name only the surfaces that are observable (script ref, bytes, interpreter), pinned by the row's companion end-to-end test. Left unfixed — no production change in P0.

2026-08-25 — phase close-out (orchestrator). Three adversarial review rounds ran: 6 → 5 → 3 CONFIRMED findings, each round's findings distinct (deepening precision, not reshaped repeats), each fixed in its own fix(p0) commit. The round budget expired before a fourth confirming round, so the orchestrator verified the round-3 fixes directly against the reviewer's required changes instead of dispatching another round — rationale: all three were narrow, mechanical, and independently checkable (P-05 during-throw observation via observedDuring; R-08 null-fallback rows {kind:"workflow", ref:""} / {kind:"prompt", engine:null}; R-04 near-miss locator owner/repo pinned to the trailing message). All three land in fix(p0): address review findings (round 3) and match the required changes exactly.

Two ADVISORY findings were applied at close-out because they protect known upcoming phases from spurious failures: R-01(c)'s plan-JSON substring probe now uses the distinctive sentinel r01-dropped-sentinel instead of the generic word scope (protects P3a's plan-shape changes), and characterization-fixture-contracts.test.ts's generic flip comments now name their flipping phases (P2a/P2c for v3-migration fixtures, P3a for the irVersion-4 assertion; the read-set test is reclassified PRESERVE). Remaining advisories recorded, not applied: R-05(a) grep-anchor placement; P-08 1-job bound cited only via fixture tests; classifyWorkflowStepUses delegation call-count assertions pin an internal seam; P-06 child-env observed via stdout log rather than a spawn seam; R-07 positional args[2] env capture couples to runAgent argument order; R-05(b) skips akmIndex where siblings call it. Later phases may harden these opportunistically when touching those files.

Gate: bun run lint green, bunx tsc --noEmit green, unit 3879 pass / 0 fail (sharded runners). The full integration suite runs as the final gate step; its result is appended below when it completes. git diff origin/release/0.9.2..HEAD -- src/ is empty — P0 changed no production code. All acceptance boxes ticked.

2026-08-26 — integration gate confirmed green. Full integration suite: 5622 pass / 57 skip / 0 fail across 419 files (sharded runners). An initial run had 15 environment-caused failures in registry-pinned-transport, registry-network-boundary, and package-launcher — all traced to the container's default Node being v22 while akm 0.9.2 requires Node >= 24 (the transport helper and npm launcher enforce it). Fixed at the environment level by installing Node v24.19.0 and making it the default; no repository change was needed and the failures predate P0 (they reproduce at the untouched baseline in a Node-22 environment). P0 gate: lint green, tsc green, unit 3879/0, integration 5622/0.

2026-08-26 — P-05 reclassification (P1b Lane C, required by D5, spec docs/plans/specs/p1b-model-extraction.md §1.2). P-05 pinned the pre-P1b MECHANISM: the workflow dispatch arm (runner.ts:534-535, :552-555 at P0 head) stamped process.env.AKM_EVENT_SOURCE GLOBALLY for the duration of an in-process workflow-task run, then restored the prior value (or deleted the key) in a finally, so a child akm invocation made by a workflow step inherited the stamp through ambient environment inheritance. D5 always scheduled this mechanism for replacement — the global mutation was a correctness liability in its own right (P0's own row named it a cross-run-leakage risk that only worked because the stamp/restore pair happened to be synchronous-safe), and the design decision (D5, phase P1b) was to thread the resolved event source through an explicit, typed ExecutionProvenanceContext instead. P1b (F-1) deletes the global process.env stamp and its finally restore outright — nothing under src/ writes process.env.AKM_EVENT_SOURCE any more (grep-provable: rg "process\.env\.AKM_EVENT_SOURCE\s*=" src/ and rg "delete process\.env\.AKM_EVENT_SOURCE" src/ both return zero hits) — and threads the value through a new optional eventSource option instead: run-task.ts's ExecutionProvenanceContext → run-workflow-task.ts → RunWorkflowOptions.eventSource → src/workflows/exec/run-workflow.ts → native-executor.ts's StepExecutionContext.eventSource → unit-dispatch.ts's UnitDispatchRequest.eventSource → exec-unit.ts's childEnv, applied to the allowlisted base only when the ambient passthrough left the name absent.

The mechanism changed; the four contract clauses P-05 protected did not:

Verified end to end by tests/integration/tasks-provenance-characterization.test.ts's flipped P-05 rows (including a REAL, non-injected runWorkflowSteps → exec-unit child observing the stamp) and by tests/integration/tasks-provenance-context.test.ts's new RunTaskOptions.provenance coverage. No production change to P0 itself; this entry documents P1b Lane C's implementation of the flip P0's own R-07/P-05 rows named as pending.

2026-08-26 — P-05 reclassified by P1b (D5). P-05 pinned the workflow arm's global process.env.AKM_EVENT_SOURCE stamp-and-restore as then-current MECHANISM; the refactor plan always scheduled D5 to replace that mechanism with an explicit ExecutionProvenanceContext threaded through dispatch. P1b flipped P-05's mechanism-level assertions per its authorized-flips table; the preserved CONTRACT — in-process execution and usage recording observe "task", no cross-run leakage (the global is now never mutated: before, during, after), child-env stamping (P-06) unchanged, pre-set ambient values still win — is pinned by the amended tests/integration/tasks-provenance-characterization.test.ts and the new tasks-provenance-context.test.ts. R-07's defect (prompt arm recording scheduled usage as "user") is fixed as planned.

2026-08-26 — R-06 close-out: the replacement is scoped to task source v4, not a flip of the row itself (P2a, docs/plans/specs/p2a-task-source-v4.md §1.5 D2-N6, §6 F-0). The R-06 row's "Flips in" cell reads "P2a (optional schedule)"; that cell names the phase that introduces the first document shape the rule does not apply to, not a change to the rule task v3 itself pins. Task source v4 (version: 4) makes schedule: OPTIONAL — an absent schedule: parses as valid and projects to triggers = { manual: true, schedules: [] } (D2-N6) — but compileTriggers (src/tasks/source-v3.ts:450-464 as of this entry — :636-651 at the R-06 row's own "verified at head" commit, before P2a's D2-N4 extraction moved code within the file; same function, unchanged body) is untouched by P2a: a version: 3 document that declares neither akm.schedule nor on: (or declares both) still fails with the byte-identical message must declare exactly one scheduling source: akm.schedule or on., still via sourceError(ctx, [], …) with the empty path, and still with the three frozen-shape assertions on the akm.schedule success arm. All three of tests/integration/tasks-scheduling-characterization.test.ts's R-06 tests (the neither-case at :45, the both-case at :57, and the akm.schedule success shape at :73, including its Object.isFrozen assertions) pass unchanged after P2a — verified as part of P2a's preservation gates. R-06 therefore stands, unflipped, for every version: 3 document; only a version: 4 document gets the new, additive optional-scheduling row (B-06/B-07 in the P2a spec's behavior table). v3's exactly-one-scheduling-source rule stands until P4 removes v3 acceptance outright — this row should not be read as "replaced" in the sense of no longer holding for any document, only as "superseded for the one new document shape that did not exist before P2a."

2026-08-28 — P4 close-out (Lane C, docs/plans/specs/p4-deletions-closeout.md §5.5): final disposition of every R row, and of the P rows P4 supersedes. P4 is the last phase in the refactor ladder; the three deletion families (§3) and the vocabulary/documentation close-out (§4) are landed at Lane C's head. This entry appends §5.5's disposition table verbatim — no pinned row's text above is edited, this is prose-only closure:

Row Final disposition
R-01 RESOLVED — P1a rejected the silent with: drop (COMPOSITION_INVALID); P2b replaced the rejection with real bindings when the target declares inputs:.
R-02 RESOLVED — P1b's prepareScriptTarget() deleted the synthetic task YAML; no src file fabricates a task-source header (tests/workflows/direct-script-typed.test.ts's source scan).
R-03 RESOLVED — P3a replaced all three nested-workflow rejections with the ONE recursive child-workflow resolver; the documented-dead duplicate went with them.
R-04 RESOLVED by deletion — P4 §3.1. The grammar, both consumers and the remote-action-acquisition-out-of-scope code are gone from src; the frozen migrator keeps a copy so it can still name the target when it blocks a file.
R-05 RESOLVED by deletion — P4 §3.3. Parse-then-reject becomes one adapter-boundary rejection; the return-vs-throw asymmetry the row pinned no longer exists.
R-06 RESOLVED by deletion — P2a made scheduling optional for task source v4; P4 §3.2 removed v3 acceptance, so the exactly-one-scheduling-source rule has no document left to apply to.
R-07 RESOLVED — P1b threaded ExecutionProvenanceContext; the prompt/command arm records "task".
R-08 RESOLVED — P1b's D8 vocabulary + read-boundary mapping. The mapping itself is a PRESERVE surface forever (row B-51).
R-09 RESOLVED at the boundary — P1b renamed stashDir → bundleDir; P4 §4.1 consolidated the stray literals onto DEFAULT_BUNDLE_NAME, whose VALUE stays "stash" for user-data compatibility. src/indexer/** was out of scope from P0 onward.
P-01 / P-02 SUPERSEDED by P4 §3.2 — unreachable from any parseable source (task source v4 accepts with: only on uses: akm/command). The seam guards are retained as invariants (P4-N4); the pinned tests are deleted with floor accounting.
P-03 SUPERSEDED by P4 §3.2 — a task document can no longer author with: on a workflow target; prepared.params is the task's defaulted declared inputs, or {}.
P-04 PRESERVED, re-fixtured — task source v4 still has a top-level env:, so the workflow-target env rejection stays reachable and stays pinned.
P-05 / P-06 / P-07 PRESERVED (P-05 reclassified by P1b's D5 — see the 2026-08-26 entries).
P-08 SUPERSEDED by P4 §3.3 — the multi-job parser's job model was pinned as the thing the P4 adapter boundary would be built on; the boundary was built by deleting it instead, per brief §10. Job-count bounds, needs validation, cycle/duplicate detection and canonical ordering are all unreachable with exactly one job.

Two dispositions above are worth flagging as observed, not merely asserted, at P4 Lane C's head: P-04's "stays pinned" held through §5.2's own INVALID_FLAG_VALUE re-coding sweep — the workflow- target env: guard's code was measured, found pinned by tests/integration/tasks-with-classification-characterization.test.ts's P-04 block, and left INVALID_FLAG_VALUE rather than recoded to COMPOSITION_INVALID as §5.2's generic target table would otherwise have applied, exactly as this table's row anticipates. R-08's "PRESERVE surface forever" held the same way against src/tasks/source/bounded-document.ts's six-throw front-end recode: the D8 legacy read mapping in src/tasks/run/task-history.ts was not among the sites touched, and its own byte-unchanged tests stayed green throughout.