akm-cli 0.9.29 → 0.9.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +10 -10
- package/ROADMAP.md +112 -0
- package/dist/commands/improve/improve-cli.js +33 -12
- package/dist/commands/sources/plugin-upgrade.js +46 -23
- package/dist/core/file-lock.js +10 -1
- package/dist/llm/client.js +33 -3
- package/dist/scripts/akm-migrate-node.js +5 -1
- package/dist/scripts/akm-migrate.js +5 -1
- package/docs/README.md +42 -41
- package/docs/agents/README.md +15 -0
- package/docs/agents/agent-install.md +14 -0
- package/docs/architecture/README.md +67 -0
- package/docs/architecture/adapters.md +272 -0
- package/docs/architecture/akm-architecture-decision-history.md +2055 -0
- package/docs/architecture/akm-core-principles.md +126 -0
- package/docs/architecture/architecture.md +579 -0
- package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
- package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
- package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
- package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
- package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
- package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
- package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
- package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
- package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
- package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
- package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
- package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
- package/docs/architecture/decisions/README.md +62 -0
- package/docs/architecture/improvement.md +440 -0
- package/docs/architecture/internals/classification.md +154 -0
- package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
- package/docs/architecture/internals/functional-contract-patterns.md +340 -0
- package/docs/architecture/internals/health-advisories.md +110 -0
- package/docs/architecture/internals/improve-workflow.md +713 -0
- package/docs/architecture/internals/indexing.md +498 -0
- package/docs/architecture/internals/registry-network-boundary.md +57 -0
- package/docs/architecture/internals/search.md +249 -0
- package/docs/architecture/internals/storage-locations.md +943 -0
- package/docs/architecture/persisted-data-compat.md +59 -0
- package/docs/architecture/reviews/env-secret-access.md +319 -0
- package/docs/architecture/runtime-boundary-design.md +153 -0
- package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
- package/docs/architecture/specs/0.9.0-decisions.md +528 -0
- package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
- package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
- package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
- package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
- package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
- package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
- package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
- package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
- package/docs/architecture/specs/di-seams-plan.md +539 -0
- package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
- package/docs/architecture/specs/fact-asset-type.md +142 -0
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
- package/docs/architecture/specs/okf-support.md +148 -0
- package/docs/architecture/specs/ref.md +329 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
- package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
- package/docs/architecture/specs/stash-organization-conventions.md +377 -0
- package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
- package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
- package/docs/architecture/specs/workflow-format-unification.md +437 -0
- package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
- package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
- package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
- package/docs/architecture/testing/testing-workflow.md +446 -0
- package/docs/architecture/workflow-engine.md +274 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/author-bundles.md +310 -0
- package/docs/guides/author-workflows.md +520 -0
- package/docs/guides/bundles.md +321 -0
- package/docs/guides/capture-knowledge.md +136 -0
- package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
- package/docs/guides/concepts.md +93 -0
- package/docs/guides/discover-and-load.md +158 -0
- package/docs/guides/getting-started.md +240 -0
- package/docs/guides/improve-the-library.md +246 -0
- package/docs/guides/knowledge-management.md +18 -0
- package/docs/guides/recipes/headless-install.md +97 -0
- package/docs/guides/recipes/website-source.md +28 -0
- package/docs/guides/run-workflows.md +279 -0
- package/docs/guides/scheduling.md +123 -0
- package/docs/guides/use-with-any-agent.md +129 -0
- package/docs/guides/wikis.md +81 -0
- package/docs/maintainers/README.md +19 -0
- package/docs/maintainers/curate-workmap.md +336 -0
- package/docs/maintainers/eval.md +3 -0
- package/docs/maintainers/local-development.md +108 -0
- package/docs/maintainers/release-checklist.md +103 -0
- package/docs/migration/README.md +2 -2
- package/docs/migration/release-notes/0.0.13.md +1 -1
- package/docs/migration/release-notes/0.1.0.md +1 -1
- package/docs/migration/release-notes/0.2.0.md +1 -1
- package/docs/migration/release-notes/0.3.0.md +1 -1
- package/docs/migration/release-notes/0.5.0.md +1 -1
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.3.md +2 -2
- package/docs/migration/release-notes/0.7.4.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +1 -1
- package/docs/migration/release-notes/0.8.0.md +1 -1
- package/docs/migration/release-notes/0.9.0.md +1 -1
- package/docs/migration/release-notes/0.9.14.md +1 -1
- package/docs/migration/release-notes/0.9.15.md +1 -1
- package/docs/migration/release-notes/0.9.16.md +1 -1
- package/docs/migration/release-notes/0.9.17.md +1 -1
- package/docs/migration/release-notes/0.9.19.md +1 -1
- package/docs/migration/release-notes/0.9.2.md +1 -1
- package/docs/migration/release-notes/README.md +27 -31
- package/docs/migration/v0.5-to-v0.6.md +759 -0
- package/docs/migration/v0.8-to-v0.9.md +1 -1
- package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
- package/docs/posts/README.md +30 -0
- package/docs/posts/akm-command-tour.md +495 -0
- package/docs/posts/akm-logo-sized.webp +0 -0
- package/docs/posts/belief-aware-memory-12.md +139 -0
- package/docs/posts/config-v2-lessons-16.md +191 -0
- package/docs/posts/improve-loop-autonomous-10.md +225 -0
- package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
- package/docs/posts/intro-part-01.md +76 -0
- package/docs/posts/intro-part-02.md +221 -0
- package/docs/posts/intro-part-03.md +190 -0
- package/docs/posts/intro-part-04.md +221 -0
- package/docs/posts/llm-mode-reflect-13.md +166 -0
- package/docs/posts/openpalm-built-on-akm.md +86 -0
- package/docs/posts/progressive-disclosure-06.md +129 -0
- package/docs/posts/proposal-safety-net-15.md +112 -0
- package/docs/posts/release-0.5.0.md +247 -0
- package/docs/posts/release-0.6.0.md +117 -0
- package/docs/posts/release-0.7.0.md +159 -0
- package/docs/posts/release-0.8.0.md +332 -0
- package/docs/posts/scattered-skills-05.md +100 -0
- package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
- package/docs/posts/team-skills-07.md +198 -0
- package/docs/posts/wiki-agent-knowledge-08.md +223 -0
- package/docs/posts/workflows-vaults-09.md +321 -0
- package/docs/product-surface.md +54 -0
- package/docs/reference/README.md +1 -2
- package/docs/reference/asset-types.md +168 -0
- package/docs/reference/cli.md +5 -6
- package/docs/reference/env-and-secrets.md +126 -0
- package/docs/reference/memory.md +124 -0
- package/docs/reference/refs.md +156 -0
- package/docs/reference/registry.md +489 -0
- package/docs/reference/website-sources.md +141 -0
- package/package.json +12 -15
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# v8 spec architecture review
|
|
2
|
+
|
|
3
|
+
major | task path has no freeze stage today, and the spec never says whether it gets one | `runPromptTask`/`runCommandTask` in `src/tasks/runner.ts` resolve engine/model/cascade against *live* `loadConfig()` synchronously inline (no compile/resolve/freeze staging, no persisted plan), while §5.4's two-stage freeze is specified purely in workflow terms (`compileResolveFreezeWorkflow`) — the spec asserts cascade.ts is "consumed at freeze (workflows) and dispatch (tasks)" but never states whether task dispatch runs compile→resolve→freeze in-memory-only or calls cascade.ts directly, skipping the asset-loader injection seam that persona/`uses:` resolution depends on. | spec §4.3, §5.4; `src/tasks/runner.ts:461-567`; `src/workflows/ir/freeze.ts:43`
|
|
4
|
+
|
|
5
|
+
major | AgentUnitExecutor seam glosses over two structurally different dispatch surfaces | Workflow dispatch already flows through `UnitDispatchRequest` (frozen `FrozenEngineSnapshot`, `IrInvocation`, `env: Record<string,string>`, journaled via `workflow_run_units`) in `src/workflows/exec/unit-dispatch.ts`, but the task-prompt path (`runPromptTask`) builds a `RunnerSpec` from live config via `resolveEngine`/`resolveLlmEngineUse` and calls `executeRunner()` directly, then writes to `task_history`/`logs.db` — a completely different type and persistence shape; "one AgentUnitExecutor, two callers" needs an adapter layer the spec doesn't name, not just a shared strategy interface. | `src/workflows/exec/unit-dispatch.ts:9-31`; `src/tasks/runner.ts:479-539`
|
|
6
|
+
|
|
7
|
+
major | ShellUnitExecutor claim ignores that the task runner's shell path is a distinct, already-hardened subsystem | `runCommandTask` already owns process-group kill-ladders, `AKM_EVENT_SOURCE` stamping, `resolveNestedAkmCommand`, and its own log/history sinks (`src/tasks/runner.ts:250-341`); folding this into one `ShellUnitExecutor` shared with the orchestrator's `Bun.spawn()` path is plausible but the spec treats it as "just" a strategy swap (§5.1) when it actually requires reconciling two independent timeout/kill/logging contracts — not costed anywhere in §9. | `src/tasks/runner.ts:273-296`; spec §5.1, §9
|
|
8
|
+
|
|
9
|
+
major | cascade module's two consumption times are not obviously bitemporally safe for `uses: tasks/<ref>` composition | A workflow step referencing `tasks/<id>` resolves that task's cascade layers at the workflow's freeze time (baked into the frozen plan), but the *same* task run standalone via `akm task run <id>` re-resolves cascade against config as of the actual run — the spec never states which config snapshot a step's `uses: tasks/<ref>` composition should read (config at workflow-freeze time vs. config at task-file's own authoring), so two runs of "the same" referenced task can diverge without either being wrong per the spec's own text. | spec §4 layers table, §5.4; `src/workflows/ir/freeze.ts:41` ("only source-to-runtime boundary")
|
|
10
|
+
|
|
11
|
+
major | persona snapshot / "show layer" reuse is directionally sound but the show layer isn't a resolver today, it's a CLI-output formatter | The provenance ceiling the spec cites is real (`src/commands/read/show.ts:433-445`, `isPrimaryStash` gate deleting `toolPolicy` for non-writable stashes) but it lives inside `akm show`'s response-shaping code, not a reusable resolution function; extracting a `{systemPrompt, toolPolicy, model, …}` snapshot for freeze means carving a pure resolver out of a command handler that currently also does display formatting — a real but unscoped refactor, understated as "consumes the show layer's verdict." | `src/commands/read/show.ts:433-445`; spec §4 item 2
|
|
12
|
+
|
|
13
|
+
minor | IrInvocation as currently typed (`engine: string` required, `model`, `timeoutMs`, `llm?`) is agent/llm-shaped only — extending it to a `{kind:"agent",…}|{kind:"shell",…}` discriminated union is the right shape given `IrRuntimeKind` already exists as `"llm"\|"agent"\|"sdk"`, but the spec should say explicitly whether `IrRuntimeKind` gets a `"shell"` member or whether shell units carry no `runner` field at all — `decodeWorkflowPlanV3`'s `validateInvocation`/`assertUnitEngineCompatibility` (`src/workflows/ir/schema.ts:481-527`) hard-requires `invocation.engine` on every unit today, so the decoder rewrite is nontrivial, not a narrow "instructions become optional" patch. | `src/workflows/ir/schema.ts:78-84, 383-452, 517-527`; spec §5.2
|
|
14
|
+
|
|
15
|
+
minor | hashVersion 5 preimage table is a clean, additive extension of the existing pattern | `step-work.ts`'s `computeStepWorkList` already documents its hash preimage exhaustively (hashVersion 4, lines 375-392) and explicitly walks through why each field is in/out; the v5 additions (target kind + ref + content hash, shell text, env literal values) fit the same discipline and the existing code's own doc comments make clear this is a well-understood seam to extend, not a risky one. | `src/workflows/exec/step-work.ts:333-392`; spec §5.3
|
|
16
|
+
|
|
17
|
+
minor | "one cascade module" name is right, but engine/model resolution logic that would feed it is currently duplicated three ways with subtly different fallback rules (`exactModel`/`effectiveTimeout` in freeze.ts, `resolveEngine`+inline overrides in runner.ts, `resolveLlmEngineUse` in engine-resolution.ts) — each has bespoke opencode-sdk-fallback handling; cascade.ts absorbing all three without losing the sdk-fallback edge cases (`freeze.ts:174-186, 199-208`) is the real engineering cost, understated by calling it "one cascade module." | `src/workflows/ir/freeze.ts:163-209`; `src/tasks/runner.ts:479-514`; spec §4 item 3
|
|
18
|
+
|
|
19
|
+
minor | env literal/ref provenance split matches an idiom the codebase already uses elsewhere (names-only hashing, resolved-value redaction sets) | `step-work.ts` already hashes `env: template.env ?? null` as ref *names* only and documents exactly the "hashing a resolved secret would leak it" rationale the spec restates in §5.5 — this is not a new pattern, it's naming an existing invariant, which is the correct minimal move. | `src/workflows/exec/step-work.ts:353-358`; spec §5.5
|
|
20
|
+
|
|
21
|
+
minor | `type: task` adapter-ordering claim is correct and lint-checkable today | `BUILTIN_ADAPTERS` in `src/core/adapter/adapters/index.ts:73-90` does put `akmWorkflowAdapter` before `akmTaskAdapter`, both before the loose `akmAdapter`, confirming the spec's M8 claim that "workflow adapter is ordered first and claims `.md` files" is grounded, not asserted. | `src/core/adapter/adapters/index.ts:73-90`; spec §3
|
|
22
|
+
|
|
23
|
+
minor | GHA-alignment naming (`uses:`/`run:`/`with:`) is clean and the mutual-exclusion rule maps directly onto a single discriminated `Target` union — no simpler alternative stands out; the one soft spot is overloading `uses: tasks/<n>` to mean "compose this task's fields into me" while `uses: workflows/<r>` means "invoke and return," two different composition semantics under one keyword, which the spec acknowledges (no nesting rule) but doesn't name as an asymmetry worth flagging to authors beyond a doc note. | spec §2.2, §5.1
|
|
24
|
+
|
|
25
|
+
minor | over-engineered: threading `llm` overrides validation (`assertUnitEngineCompatibility`, `validateLlmOverrides`) through a *shell* invocation union member that never carries them is dead branch surface — the discriminated union (§5.1 `{kind:"shell",...}`) should structurally exclude `llm`/`engine` fields rather than relying on decoder-time cross-checks the way today's single-shape `IrInvocation` must; the spec doesn't say the shell variant drops the whole `invocation` wrapper, so decoder complexity may grow rather than shrink under v4. | `src/workflows/ir/schema.ts:481-498, 517-527`; spec §5.2
|
|
26
|
+
|
|
27
|
+
minor | under-specified: scheduler-id mapping (`/` → `--` with collision lint) and per-engine capability-declaration shape are both explicitly deferred to the implementer (§11 "Open items") — reasonable to defer, but subdir task ids interacting with `validateTaskId`'s current rejection of `/` (cited in §9) means the migration step and the new id grammar must land atomically, which isn't sequenced anywhere in §8's migration steps. | spec §8, §11; `src/tasks/task-id.ts` (not read in depth but referenced by spec)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Audit of v8 §11 findings cross-reference
|
|
2
|
+
|
|
3
|
+
Spec: /home/user/akm/docs/architecture/specs/task-workflow-format-unification.md (v8)
|
|
4
|
+
Baseline: claude/akm-markdown-tasks-history-75l6du @ current HEAD (d8095a0)
|
|
5
|
+
Consolidated findings: scratchpad/review-consolidated.md
|
|
6
|
+
|
|
7
|
+
Method: for each finding, located v8's claimed resolution via §11's table, read the
|
|
8
|
+
referenced section in full, and where v8 makes a factual claim about current code
|
|
9
|
+
(to justify the design), re-verified that claim by reading the cited file/lines
|
|
10
|
+
directly. Flagged anything that only restates the finding, that is logically
|
|
11
|
+
incomplete relative to the finding's actual scope, or that conflicts with another
|
|
12
|
+
part of v8 or with a documented architectural decision elsewhere in the codebase.
|
|
13
|
+
|
|
14
|
+
## Verified genuinely resolved (design-level; spec, not code, so "resolved" = coherent design that actually addresses the mechanism, not just names it)
|
|
15
|
+
|
|
16
|
+
- C1 (output collision): compile.ts:204 (`step.output` → `outputSchema`) and
|
|
17
|
+
compile.ts:229 (`unit?.output` → `schema`) confirm the two real, distinct
|
|
18
|
+
schema slots v8's §7 two-homes design targets. Accurate and complete.
|
|
19
|
+
- C2 (shell units in IR): unit-dispatch.ts:19 confirms `engine: FrozenEngineSnapshot`
|
|
20
|
+
is non-optional today, matching v8's premise; §5.2's discriminated-union +
|
|
21
|
+
per-kind engine resolution is a real fix, not a rename.
|
|
22
|
+
- C3 (hash preimage): hashVersion 5 table in §5.3 covers every named gap (run: text,
|
|
23
|
+
shell/cwd, uses: ref + content hash, env literals) and the freeze-time-append
|
|
24
|
+
design in §2.3 correctly resolves the "no single seam" objection.
|
|
25
|
+
- C4 (cascade layers): config-schema.ts:102-108 confirms `DefaultsSchema` is
|
|
26
|
+
`.passthrough()` with no `model` field, matching v8's claim; §4's "build, not
|
|
27
|
+
expose" inventory (6 concrete numbered work items) is substantive, not hand-wavy.
|
|
28
|
+
- M2 (placeholder grammar): agent-dispatch.ts:67-72 confirms `fillPlaceholders`
|
|
29
|
+
only substitutes positional `{{\d+}}` (matches v7-era `{{0}}` bug claim); named
|
|
30
|
+
`{{name}}` is recognized only by `extractParameters` (renderers.ts) for metadata,
|
|
31
|
+
never filled — confirms the bug is real and v8's with:/with.arguments design
|
|
32
|
+
addresses it.
|
|
33
|
+
- M3 (env additive): compile.ts:46 confirms `env?: string[]` — IR literally cannot
|
|
34
|
+
carry literal values today. §5.5's provenance split is a real fix.
|
|
35
|
+
- M4 (alias portability): model-aliases.ts confirms `BUILTIN_ALIASES` has only
|
|
36
|
+
`claude`/`opencode` columns (no `"*"`), while the *global* config-level alias
|
|
37
|
+
table already supports a `"*"` fallback (config-schema.ts / model-aliases.ts
|
|
38
|
+
doc comment) — so v8's proposed "give builtins a `*` column too" is consistent
|
|
39
|
+
with an existing pattern, not novel invention.
|
|
40
|
+
- M6 (dead kind-gate): freeze.ts:67-73 confirms the exact dead-code shape
|
|
41
|
+
(`llm` computed only when `kind==="llm"`, then guarded on `kind!=="llm"`).
|
|
42
|
+
Accurate; the capability-notice replacement in §4 item 4 is coherent.
|
|
43
|
+
- M9 (akm-bin resolution) — see PARTIAL below.
|
|
44
|
+
- M11 (freeze IO / lint never freezes): grep confirms `compileResolveFreezeWorkflow`
|
|
45
|
+
has exactly one call site (runs.ts:241). The two-stage compile/resolve/freeze +
|
|
46
|
+
lint dry-freeze design in §5.4 is a real structural fix.
|
|
47
|
+
- M12 (defaults.on_error exists today): program/schema.ts:204-211 confirms
|
|
48
|
+
`ProgramDefaults.onError` already exists at document level, exactly as the
|
|
49
|
+
finding states. §4's decision to keep on_error/retry as value fields is a
|
|
50
|
+
direct, correct response.
|
|
51
|
+
- M8 (adapter ordering): akm-workflow-adapter.ts:66-70 confirms `isWorkflowFile`
|
|
52
|
+
returns true whenever `type` is undefined or `"workflow"` — any `.md` without a
|
|
53
|
+
contrary `type:` is claimed by the workflow adapter, confirming the collision.
|
|
54
|
+
Making `type: task` mandatory (§3) is a workable, not hand-waved, fix.
|
|
55
|
+
|
|
56
|
+
## Problems found
|
|
57
|
+
|
|
58
|
+
C5 | contradicts | v8 §5.6 proposes a new persisted per-bundle trust grant (`bundles.<name>.allowScriptExecution`, settable via `akm setup`/`akm config set`) to unlock third-party script execution — but src/core/activation-policy.ts explicitly documents a 2026-07-14 decision that the "installation is not activation" work "ships no new trust / approval / security machinery: no labeling, action clamps, confirm prompts, digests, trust records, or persisted `workspace_bindings`," and the existing analogous `tools:` provenance ceiling (show.ts:433-445) has no override at all — it unconditionally strips `toolPolicy` for non-primary-stash assets. v8's claimed "same ceiling philosophy" is therefore inaccurate: the tools: ceiling is a hard, ungrantable ceiling, while the new mechanism is an invertible, persisted, named per-bundle opt-in — a materially bigger and different kind of security surface than the finding's own comparison implies, and one that runs directly against a decision already on record in the codebase.
|
|
59
|
+
|
|
60
|
+
M13 | partial | v8's §11 row points only to "§3 (improve excluded from task bodies)," which resolves the improve-pipeline half of the finding, but the finding's other half — "the akm adapter special-cases type:'task' as pure YAML so markdown checks never fire" (akm-adapter.ts:404-412, confirmed: `parsed = { data, content: raw, frontmatter: null }` for `type==="task"`, explicitly to suppress `missing-updated`) — is never revisited. v8's own §3 examples give tasks substantial free-form prose bodies that are now the literal executable prompt, but no change to the lint/OKF parse path is proposed to give that prose the same base-check coverage other markdown assets get (frontmatter-shaped checks like `unquoted-colon`/`missing-updated` still never fire on tasks); only `stale-path`/`missing-ref` (body-scoped, frontmatter-independent) survive.
|
|
61
|
+
|
|
62
|
+
M9 | partial | §5.7's fix ("PATH prepend... replacing the argv-rewrite that shell text would defeat") glosses over why `resolveNestedAkmCommand` was built as an argv splice in the first place: `resolveAkmInvocation()` (resolve-akm-bin.ts) can return a *multi-element* invocation (e.g. a node/bun launcher + script path), not always a single standalone binary. A bare PATH prepend only works when there is one real executable file to point `PATH` at; the multi-arg-launcher case needs a synthesized shim (a generated `akm` script that re-invokes `node <path>`), which is materially more than "PATH prepend" and isn't mentioned.
|
|
63
|
+
|
|
64
|
+
Minor findings with no resolution anywhere in v8 (contradicts §11's blanket "all resolved or explicitly costed" claim and the Minors row's non-exhaustive listing):
|
|
65
|
+
|
|
66
|
+
- "§3.5 embodiment sentence reads backwards (--model wins over hint)" (1/3) — no trace of this in v8; the section was restructured away but the underlying precedence claim about `--model` vs. an alias/tier hint is never restated or corrected anywhere in v8's aliasing text (§4 item 5).
|
|
67
|
+
- "Value fields on `uses: workflows/*` tasks are structurally inert (run-workflow takes ref+params only)" (1/3) — not mentioned; §2.2's table gives `with:` → declared param flags for `uses: workflows/<r>` but never addresses whether cascade value fields (timeout, model, etc.) set on such a task do anything, which was the actual complaint.
|
|
68
|
+
- "uses: table covers 5 of ~15 placement types; no rule for the rest" (1/3) — not mentioned anywhere in v8.
|
|
69
|
+
- "single-file provenance lost" (part of the 256 KiB composition finding, 1/3) — §5.4 addresses the 256 KiB cap-breach half ("carry the existing... cap with a lint warning at 80%") but not the provenance-loss half of the same finding.
|
|
70
|
+
|
|
71
|
+
## Notes on things that check out but are worth flagging as "costed, not fixed" (v8 is honest about these, no complaint)
|
|
72
|
+
|
|
73
|
+
- M4/§4 item 6: gate judge (`judgeEngine`) explicitly stays outside the cascade in 0.9.0 — correctly flagged as deferred rather than claimed resolved.
|
|
74
|
+
- M7/§8: migration section is a genuine rewrite against the real migrator, spot-checked one mapping (`name:` → body H1) is plausible; not exhaustively re-verified against `task-target-ref-migration.ts` line-by-line given time budget.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Security/migration review — task-workflow-format-unification.md v8 (d8095a0), baseline 71bb686
|
|
2
|
+
|
|
3
|
+
- critical | §5.6 grant mechanism does not exist anywhere in the codebase | `allowScriptExecution` / `bundles.<name>.allowScriptExecution` has zero hits in `src/`, so today "third-party bundle → script asset" is unconditionally executable (script `run:`/`setup:` is already advisory-only per `src/core/activation-policy.ts` comments) — the whole §5.6 line is unbuilt, not just "resolve-time enforceable," and no code path currently gates it | grep across `src/` for `allowScriptExecution`/`ScriptExecution`; `src/core/activation-policy.ts` rules 1-4 (env/task/write-activation only, no script-execution rule).
|
|
4
|
+
- critical | §5.6's reused "ceiling philosophy" (tools: provenance gate, M5) is unverified against code | The spec says script-execution enforcement reuses "the writable-vs-third-party provenance ceiling for self-declared `tools:`" that "already lives" at the show layer, but no such ceiling/gate keyed on writable-vs-registryId for `tools:` was found anywhere under `src/integrations/agent/`, `src/core/adapter/`, or `src/core/asset/` — if M5's premise is wrong, §5.6's plan to "consume the show layer's verdict" has nothing to consume and must build the primitive from scratch, changing the cost estimate | grep for `provenance ceiling`/`toolPolicy` in agent/adapter trees turned up no writable/registryId-keyed logic; only `activation-policy.ts` rule 4 (`isSourceWriteActivated`) and rule 1 (env dangerous-key) exist as analogous patterns.
|
|
5
|
+
- major | §5.6 composition-chain bypass is not addressed by the per-bundle grant design | A writable task composing `uses: tasks/<ref>` where the referenced task (in a third-party bundle) itself targets `uses: scripts/<n>` resolves recursively (§5.1 `{ kind: task, ref }` "resolves recursively once") — the spec never states whose bundle-grant applies (the composing writable task's bundle, which has no grant, or the referenced task's own third-party bundle, which needs one) or how §5.4's resolve stage attributes the grant when task-A (bundle X, writable) pulls task-B (bundle Y, third-party, ungranted) that pulls script-C — this is exactly the "composition chains" bypass route named in the audit brief and v8 gives it no explicit resolution rule | §5.1 recursive task resolution; §5.6 (grant keyed by bundle, not by resolved-asset chain); §2.3 "no nesting" only forbids workflow-in-step, not task-in-task depth-1 recursion through third-party bundles.
|
|
6
|
+
- major | §5.6 grant granularity is per-bundle, not per-asset, so one grant silently activates every future script in that bundle including ones added after the grant (bundle-update bypass) | The grant text says "settable... by `akm config set`" against `bundles.<name>.allowScriptExecution: true` — a boolean on the whole bundle. §5.6 does not pin the grant to a content hash or a specific script set, so `akm update` on a third-party bundle can swap in new/modified script content (or a modified `run:`/`cwd:`) that inherits the prior grant with no re-confirmation — the audit brief's "bundle update swapping script content after grant" bypass is not closed by the design as stated | §5.6 grant text (`bundles.<name>.allowScriptExecution: true`); no mention of grant invalidation on content change; contrast with §5.3's hash-preimage discipline for *dispatch* identity, which has no counterpart for *authorization* identity.
|
|
7
|
+
- major | §5.6 cron-fire-time vs sync-time grant evaluation is unspecified, and §5.4 says resolution happens at "resolve time," which for the task path is `task sync`/lint, not `task run` fire time | The audit brief specifically asks whether grants are evaluated at sync time (when the OS entry is installed) or fire time (when cron actually invokes `akm task run <id>`); §5.6 says "enforced at both executors" and "applied at resolve time (§5.4)" but §5.4's two-stage compile/resolve/freeze pipeline is described for workflows, and the task path's own freeze/resolve moment relative to `sync` vs the scheduler's `task run` invocation is never pinned — if the grant is checked at `sync` time only, revoking `allowScriptExecution` after sync leaves the OS-level cron entry still firing the script, since §5.2 says cron enable/disable is a separate `enabled:` gate, not a script-authorization gate | §5.6; §2.3 ("Trigger keys ... consumed only by `akm task sync` and, at fire time, the task runner's enabled gate `runner.ts:169`" — this passage explicitly documents `enabled` as fire-time-checked but says nothing analogous for the script-execution grant).
|
|
8
|
+
- major | §5.5 "literal entries ... never redacted" collides with `param-secrets.ts`'s own heuristic scanner for exactly this shape of leak, and the spec does not reconcile the two | `param-secrets.ts` exists specifically because author-written values (there: workflow params) that "look like credentials" are dangerous when they cannot be redacted — it scans key names and value entropy and warns. §5.5 freezes `env:` literal values into the plan with the identical non-redactable contract ("frozen into the plan, hashed, shown by `brief`, never redacted") but the spec's decision log gives no equivalent heuristic-warning obligation for literal env entries the way `param-secrets.ts` already provides for params — an author who writes `env: [{ API_KEY: "sk-live-..." }]` (a plausible mistake, not even malicious) gets zero warning at freeze/lint time despite the codebase already having the exact detector (`detectSecretShapedParams`) that could be pointed at literal env values too | `src/workflows/exec/param-secrets.ts` (rationale + `detectSecretShapedParams`); spec §5.5 (no mention of applying the existing secret-shape heuristic to literal env entries); §5.4 (lint's dry-freeze would be the natural hook and isn't used for this).
|
|
9
|
+
- major | §5.5's `AKM_ITEM` (JSON) env injection has no shell-escaping/quoting story and no code precedent to check against | `AKM_ITEM` does not exist anywhere in `src/` yet (grep confirms zero hits) — this is wholly new plumbing proposed by §5.5 ("a `map:` step with a shell target receives `AKM_ITEM` (JSON) and `AKM_ITEM_INDEX` per unit"). JSON serialized into a process env var is safe from *shell metacharacter* injection only if the executor sets it via the `env` object passed to `spawn`/`Bun.spawn` (not string-interpolated into a shell command line); the spec doesn't say which, and existing shell-invoking code in this codebase (`src/tasks/backends/exec-utils.ts` `spawnCommand`) passes argv arrays with no shell, which is the safe pattern, but nothing pins §5.5's `AKM_ITEM` assembly to that pattern rather than to a `run:` string that a script might `eval`/interpolate itself (e.g., a script doing `node -e "$AKM_ITEM"` would be JSON-injection-shaped if the JSON contains `"`/backtick sequences and the script author naively string-interpolates it into a nested command) | §5.5 "Env assembly moves per-unit ... receives `AKM_ITEM` (JSON)"; no code precedent (`grep AKM_ITEM src/` → 0 hits); `src/tasks/backends/exec-utils.ts` `spawnCommand` shows the codebase's existing safe argv-array convention that §5.5 should but does not explicitly commit to.
|
|
10
|
+
- major | §8 migration plan does not fit the actual 71bb686 backup/journal model for the .yml→.md file-level transform | `migration-backup.ts`'s manifest/journal machinery (`ARTIFACT_NAMES`, `MigrationBackupManifest`, apply/restore journal + sentinel) covers exactly four opaque artifacts — `config.json`, `state.db`, `workflow.db`, `index.db` — and has no concept of per-bundle-file changes; the *actual* hardened per-file transform pattern in this codebase is `task-target-ref-migration.ts`'s plan/apply split (read-before-write byte-fencing via `current.equals(rewrite.before)`, single atomic rename per file, no cross-file transaction). §8's `.yml`→`.md` conversion is a two-file operation (create the new `.md`, remove the old `.yml`) that this per-file model does not support atomically: an interruption between writing the new `.md` and removing the old `.yml` leaves both present, which the spec's own "`<id>.yml` + `<id>.md` collide on one conceptId → same named error" rule would then fire on the *migrator's own* half-applied state, turning an interrupted migration into a self-inflicted hard error rather than a resumable step | `scripts/akm-migrate/migration-backup.ts` (ARTIFACT_NAMES, manifest scope); `scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts` (per-file plan/apply, single-file atomicity only); spec §8 tombstone-collision rule.
|
|
11
|
+
- major | §8's "vendored, frozen copy of the v2 task parser" fix is not yet applied at 71bb686 — the cited defect is confirmed live, not already resolved | `scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts` line 25 currently does `import { parseTaskDocument } from "../../../../src/tasks/parser"` — the live, non-frozen parser — exactly the M7 finding the spec describes as needing a fix "as part of this work." This is consistent with the spec's own claim (not a contradiction), but it means the "frozen-migrator principle" is presently violated in the baseline the spec is built on, and v8 gives no interim mitigation for the window between baseline and when the vendoring lands — any `src/tasks/parser` change merged before the vendoring work starts will retroactively break `akm-migrate` silently, since nothing in the migrator's test suite pins the parser's frozen behavior independent of `src/tasks/parser` | `scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts:25`; spec §8 first bullet and §11 M7 row.
|
|
12
|
+
- major | §8 read-only bundle handling for the .yml tombstone/collision rule doesn't address the same read-only-bundle carve-out already coded for the *existing* migration | `task-target-ref-migration.ts`'s `planTaskTargetRefMigration` explicitly skips non-writable bundles and instead *warns* that their v1 `.yml` tasks "will not run after upgrade" (never rewritten, never deleted) — this is the precedent §8's new `.yml`→`.md` conversion must follow for read-only bundles, but §8 states only "task discovery keeps matching `tasks/*.yml` permanently, as a diagnostic" and "sync/doctor/lint each hard-error" without saying whether that hard-error applies to a read-only third-party bundle the operator cannot fix (contradicting the existing precedent of warn-and-continue for exactly that case) — operators with a read-only bundle carrying legacy `.yml` tasks would move from "warned, keeps working" (current) to "hard-errors sync/doctor/lint" (v8), a behavior regression not costed anywhere in §9 | §8 tombstone rule; `task-target-ref-migration.ts` lines 253-271 (existing read-only warn-and-skip precedent); spec §9 cost inventory has no line for this regression.
|
|
13
|
+
- minor | §2.4 lenient placeholder filling is prompt-injection-adjacent but bounded by scope, not fully safe | Leaving unmatched `{{name}}`/`$ARGUMENTS`/`$1`-`$9` verbatim (rather than erroring) means an operator who edits `with:` and mistypes a key, or pulls a shared command asset whose placeholder grammar drifted, gets a raw `{{typo}}` or literal `$3` byte-string injected into the agent prompt with no runtime signal beyond a lint warning that a human must have already run and read — this is "silent garbage into an agent prompt" as the audit brief frames it; it's a correctness/quality risk more than an injection vector since the text is author-controlled, not attacker-controlled, but shared/imported command templates make the author and the operator different trust boundaries, and §2.4 doesn't add any freeze-time notice surfaced to the *operator* (only lint, which is opt-in) | §2.4 ("An unmatched placeholder or unused `with:` key is a lint warning and is left verbatim — not a runtime hard error"); §5.4 (lint dry-freeze exists but running lint is not mandatory before dispatch).
|
|
14
|
+
- minor | §5.7 PATH-prepend hijack risk is named but Windows semantics are not actually specified | §5.7 says bare `akm` resolves "by PATH prepend in the child environment" replacing the argv-rewrite, and separately flags Windows via `schtasks`/`PATHEXT`/`COMSPEC` handling elsewhere in the codebase (`redaction.ts` allowlists `PATHEXT`, `COMSPEC`, `WINDIR` explicitly, suggesting Windows env quirks are already a known sharp edge) — but §5.7 never states whether the prepend uses `;`-joined `Path`/`PATH` (Windows env vars are case-insensitive and there can be both `Path` and `PATH` keys colliding) nor whether a child script that does its own `where akm`/`akm.cmd` shim resolution could still pick up a stale/hijacked entry earlier in the *original* PATH the prepend is supposed to shadow (prepending only shadows if the child's PATH lookup honors first-match order, which is true for `cmd.exe` but not guaranteed for every shell/script interpreter a `scripts/` asset might invoke) | §5.7; `src/core/redaction.ts` PATHEXT/COMSPEC/WINDIR allowlist entries (evidence Windows env handling is already fragile in this codebase); no cross-reference to `src/tasks/resolve-akm-bin.ts`'s existing Windows-aware absolute-path resolution, which §5.7 could reuse instead of a PATH string prepend.
|
|
15
|
+
- minor | §5.5 env-literal redaction carve-out has no cross-check against `redactSensitiveValue`'s recursive, boundary-agnostic redaction, creating a silent divergence between two redaction call sites | `src/core/redaction.ts`'s `redactSensitiveValue` recursively redacts every string leaf of a structured value using `sensitiveValues` before it "crosses a durable/output boundary" — a general-purpose function with no knowledge of the env literal/ref split §5.5 introduces; if any caller applies `redactSensitiveValue` to a frozen plan (or its env-literal entries) using a `sensitiveValues` set built only from ref-sourced secrets (per §5.5's "never redacted" contract for literals), literals are correctly preserved — but if a *different* caller (e.g. a generic log-sanitization pass) instead builds `sensitiveValues` more broadly and includes literal values, §5.5's "never redacted" guarantee silently breaks for that surface with no test/contract in the spec pinning which call sites must respect the split | `src/core/redaction.ts:385-398` (`redactSensitiveValue`, generic/recursive, no literal/ref awareness); spec §5.5 (states the contract but does not name which call sites must special-case literals vs which already handle the split correctly).
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Adversarial review — task-workflow-format-unification.md v9 (round 2, cold read)
|
|
2
|
+
|
|
3
|
+
Scope: read the spec cold, then verified its file:line evidence against the actual
|
|
4
|
+
tree at HEAD (`ec5d062`, branch `claude/akm-markdown-tasks-history-75l6du`) for
|
|
5
|
+
src/workflows, src/tasks, src/core, src/integrations/agent, scripts/akm-migrate.
|
|
6
|
+
Most citations were spot-checked directly; two rounds of prior Sonnet panel
|
|
7
|
+
review already landed on v8→v9, so this pass hunts specifically for what those
|
|
8
|
+
missed rather than re-litigating settled findings (all of which I re-verified
|
|
9
|
+
and found accurate: freeze.ts:50/67-73 dead llm guard, config-schema.ts
|
|
10
|
+
DefaultsSchema passthrough + no model field + defaults.llm/agent/improve
|
|
11
|
+
hard-rejected at line ~234-241, show.ts:433-445 tools: ceiling, runner.ts:592-593
|
|
12
|
+
raw-frontmatter bug, activation-policy.ts's no-new-trust-machinery decision,
|
|
13
|
+
task-target-ref-migration.ts:25 importing the live `src/tasks/parser`,
|
|
14
|
+
compile.ts:204/229 output vs outputSchema split, param-secrets.ts's
|
|
15
|
+
detectSecretShapedParams, model-aliases.ts's claude/opencode-only columns,
|
|
16
|
+
step-work.ts hashVersion 4 preimage, WORKFLOW_IR_VERSION currently 3).
|
|
17
|
+
|
|
18
|
+
## Findings
|
|
19
|
+
|
|
20
|
+
### MAJOR — §2.4's cited "current filler" evidence points at the wrong function; the actual bug lives in a different, unmentioned call site
|
|
21
|
+
|
|
22
|
+
§2.4 states: "the current filler substitutes only positional `{{0}}` forms
|
|
23
|
+
leniently (`src/output/renderers.ts:164-186` — a live bug fixed by this work)."
|
|
24
|
+
|
|
25
|
+
`src/output/renderers.ts:164-186` is `extractParameters()`, called only from
|
|
26
|
+
`commandMdRenderer.buildShowResponse` (line 235) to populate the `parameters`
|
|
27
|
+
field shown by `akm show`. It does not fill or substitute anything — it just
|
|
28
|
+
regexes the template for `$ARGUMENTS`/`$1-9`/`{{name}}` names for display.
|
|
29
|
+
|
|
30
|
+
The actual filler with exactly the described `{{0}}`/`{{1}}`-only lenient
|
|
31
|
+
behavior is `fillPlaceholders()` in `src/commands/agent/agent-dispatch.ts:67-72`,
|
|
32
|
+
used by `akm agent --command <ref>` dispatch (a CLI entry point the spec never
|
|
33
|
+
mentions). This is a load-bearing citation — it's the concrete evidence
|
|
34
|
+
anchoring §2.4's entire contract and the §9 "inherited bugs fixed en route"
|
|
35
|
+
line — and it misdirects an implementer to a display-only function while
|
|
36
|
+
leaving the actual buggy code, and its actual call site, uncited. It also
|
|
37
|
+
hides a real open question the spec doesn't address: does the new
|
|
38
|
+
cascade-driven `with:`/`$ARGUMENTS`/`{{name}}` filler specified in §2.4
|
|
39
|
+
replace `agent-dispatch.ts`'s `fillPlaceholders`, or do the two now coexist
|
|
40
|
+
with different (old lenient-`{{0}}`-only vs new full-grammar) semantics for
|
|
41
|
+
the same command asset type depending on dispatch path? The spec is silent.
|
|
42
|
+
|
|
43
|
+
Refs: `docs/architecture/specs/task-workflow-format-unification.md:113-114`,
|
|
44
|
+
`src/output/renderers.ts:164-186,220-238`, `src/commands/agent/agent-dispatch.ts:14-16,63-72`.
|
|
45
|
+
|
|
46
|
+
### MAJOR — §3's "recognition requires `type: task`" evidence covers only bundle-root probing; the actual per-file indexer classifier that types ordinary `tasks/*.md` files has no task branch at all, and has an ordering hazard the spec's own example would trip
|
|
47
|
+
|
|
48
|
+
§3 argues residence-only recognition is unimplementable "because the workflow
|
|
49
|
+
adapter is ordered first and claims `.md` files without a contrary `type:`"
|
|
50
|
+
citing `src/core/adapter/adapters/index.ts:73-90`. That citation (plus
|
|
51
|
+
`akm-workflow-adapter.ts`'s `isWorkflowFile`, which returns true when
|
|
52
|
+
`type === undefined`) is accurate, but it is evidence about **single-file
|
|
53
|
+
bundle-root detection** (`akmWorkflowAdapter`/`akmTaskAdapter` `looksLikeRoot`,
|
|
54
|
+
i.e. "is this repo root itself one task/workflow file") — a different code
|
|
55
|
+
path from the one that actually assigns a `type` to every ordinary file inside
|
|
56
|
+
a ordinary akm stash during `akm index`, which is `src/indexer/walk/matchers.ts`
|
|
57
|
+
(consumed directly by `akmAdapter`, per `akm-adapter.ts:27`).
|
|
58
|
+
|
|
59
|
+
In that classifier:
|
|
60
|
+
- `classifyBySmartMd` (matchers.ts:199-248) special-cases `fm.type === "workflow"`
|
|
61
|
+
(line 223-225) but has **no `fm.type === "task"` branch at all** today.
|
|
62
|
+
- The `tasks/` directory rule (`DIR_TYPE_MAP`, matchers.ts:98-105) only tests
|
|
63
|
+
`ext === ".yml"` — a `tasks/<id>.md` file gets **zero** directory-based
|
|
64
|
+
specificity from either `classifyByDirectory` or `classifyByParentDirHint`
|
|
65
|
+
today, contrary to what "residence is a lint expectation" implies is already
|
|
66
|
+
meaningfully wired.
|
|
67
|
+
- Absent a new, correctly-ordered `type === "task"` branch, the classifier's
|
|
68
|
+
existing `"agent" in fm → { type: "command" }` rule (matchers.ts:234-236)
|
|
69
|
+
fires first and would misclassify the spec's *own* first example task (§3,
|
|
70
|
+
`agent: agents/reviewer` top-level key) as a command, exactly the kind of
|
|
71
|
+
silent miswiring the workflow branch at line 223 was positioned early to
|
|
72
|
+
avoid.
|
|
73
|
+
|
|
74
|
+
§9's cost-inventory line ("markdown task recognition (`type: task`)") doesn't
|
|
75
|
+
name matchers.ts, the DIR_TYPE_MAP fix, or this ordering hazard, so an
|
|
76
|
+
implementer following only the cited evidence can miss the actual mechanism
|
|
77
|
+
that has to change and can reproduce the ordering bug live in the spec's own
|
|
78
|
+
worked example.
|
|
79
|
+
|
|
80
|
+
Refs: spec `docs/architecture/specs/task-workflow-format-unification.md:135-139`;
|
|
81
|
+
`src/indexer/walk/matchers.ts:98-105,199-248`;
|
|
82
|
+
`src/core/adapter/adapters/akm-adapter.ts:27`;
|
|
83
|
+
`src/core/adapter/adapters/akm-workflow-adapter.ts:69`.
|
|
84
|
+
|
|
85
|
+
### MINOR — "document defaults" layer's applicability to a bare (non-referenced) task is left to inference
|
|
86
|
+
|
|
87
|
+
§4's six-layer cascade table lists `document defaults:` as a layer distinct
|
|
88
|
+
from `uses: tasks/<ref>` and `step/task keys`, but §3's task schema shows no
|
|
89
|
+
`defaults:` sub-block for a standalone task file — only workflows have
|
|
90
|
+
`asset.document.defaults` today (`freeze.ts:52`). It's a reasonable inference
|
|
91
|
+
that "document defaults" is workflow-only and collapses/no-ops for a bare
|
|
92
|
+
task, but the spec never says so explicitly, and a task that both composes
|
|
93
|
+
another task (`uses: tasks/<ref>`) *and* wants its own "defaults for this
|
|
94
|
+
document" layer (as opposed to its own directly-set fields) has no described
|
|
95
|
+
home. Low risk — an implementer would likely get this right by analogy to the
|
|
96
|
+
existing `documentDefaults` special-casing in freeze.ts — but it's a genuine
|
|
97
|
+
gap in an otherwise very precisely specified cascade.
|
|
98
|
+
|
|
99
|
+
Refs: spec §4 layers table (line 238); `src/workflows/ir/freeze.ts:52`.
|
|
100
|
+
|
|
101
|
+
### MINOR — script/task "resolved content hash" for the tools: ceiling doesn't state whether frozen workflow plans re-read script bytes at dispatch
|
|
102
|
+
|
|
103
|
+
§5.3's hash preimage records "target kind + `uses:` ref + resolved content
|
|
104
|
+
hash" for re-dispatch invalidation, implying script *bytes* are not made
|
|
105
|
+
durable in the plan the way command templates are (§2.4 says command
|
|
106
|
+
templates are filled and frozen). If a script referenced by a frozen workflow
|
|
107
|
+
plan is edited between freeze and (possibly much later, queued) execution,
|
|
108
|
+
the plan's content hash goes stale for journal/idempotency bookkeeping, but
|
|
109
|
+
the spec doesn't say whether execution reads the live file (current bytes) or
|
|
110
|
+
a frozen copy, and doesn't note this as a deliberate asymmetry vs. commands.
|
|
111
|
+
Not a security issue (the ceiling only gates non-primary-stash sources, and
|
|
112
|
+
editing your own primary-stash file is expected), but worth one sentence so
|
|
113
|
+
an implementer doesn't have to guess whether "resolved content hash" implies
|
|
114
|
+
plan-embedded bytes.
|
|
115
|
+
|
|
116
|
+
Refs: spec §5.3 preimage table (line 368); §2.4 (lines 126-128, "filling
|
|
117
|
+
happens at freeze... producing the prompt string").
|
|
118
|
+
|
|
119
|
+
## Verdict
|
|
120
|
+
|
|
121
|
+
No criticals found. The two majors above are citation/evidence gaps rather
|
|
122
|
+
than design contradictions — the underlying design decisions (§2.4's contract,
|
|
123
|
+
§3's `type: task` requirement) are themselves sound and match the codebase's
|
|
124
|
+
real needs; what's wrong is that the evidence anchoring them points at the
|
|
125
|
+
wrong or incomplete code, which is exactly the kind of thing this spec has
|
|
126
|
+
otherwise been extremely disciplined about getting right (nearly every other
|
|
127
|
+
citation checked out verbatim, including several with exact line numbers).
|
|
128
|
+
An implementer working strictly from the cited evidence would fix the wrong
|
|
129
|
+
placeholder-filler function and would very plausibly ship a task-recognition
|
|
130
|
+
patch that misclassifies its own worked example. Both are cheap to fix in the
|
|
131
|
+
spec (retarget the citations, name matchers.ts + DIR_TYPE_MAP + the ordering
|
|
132
|
+
requirement) before implementation starts.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Round-2 verification of v9 against round-1 Sonnet panel (coverage/architecture/adversarial/security)
|
|
2
|
+
|
|
3
|
+
Spec: docs/architecture/specs/task-workflow-format-unification.md, v9 (ec5d062), branch
|
|
4
|
+
claude/akm-markdown-tasks-history-75l6du. Baseline for code claims: same checkout.
|
|
5
|
+
|
|
6
|
+
Method: for every critical/major in the four r1 reports, located the v9 text via §11's
|
|
7
|
+
cross-reference table, read the cited section in full, and where the fix leans on a
|
|
8
|
+
factual code claim, re-verified the claim by reading the cited file. Then checked the
|
|
9
|
+
new text against the rest of v9 for reintroduced contradictions, with extra scrutiny on
|
|
10
|
+
the five areas called out (§5.6 ceiling, persona-snapshot hash, `with:` merge, tombstone
|
|
11
|
+
+ crash-window ordering, shim vs resolve-akm-bin.ts).
|
|
12
|
+
|
|
13
|
+
## Criticals — all four verified fixed, no reintroduced contradiction
|
|
14
|
+
|
|
15
|
+
- **coverage C5** (§5.6 grant contradicts activation-policy's no-new-trust decision):
|
|
16
|
+
v9 deletes the `bundles.<name>.allowScriptExecution` grant entirely and replaces it
|
|
17
|
+
with exact inheritance of the existing `tools:` provenance ceiling (primary-stash-only,
|
|
18
|
+
no override, fail-closed) — verified `show.ts:433-445` really has no override
|
|
19
|
+
(`if (response.toolPolicy !== undefined && !isPrimaryStash) delete ...`), matching v9's
|
|
20
|
+
description exactly. `activation-policy.ts`'s "no new trust machinery" decision (header
|
|
21
|
+
comment, 2026-07-14) is respected: zero new config, zero persisted state. Fixed.
|
|
22
|
+
- **security "grant mechanism doesn't exist" / "ceiling philosophy unverified"**: moot —
|
|
23
|
+
v8's grant is withdrawn. The second half of this finding (reviewer claimed the `tools:`
|
|
24
|
+
ceiling "was not found" in code) is factually wrong — it exists at `show.ts:433-445` —
|
|
25
|
+
and v9 §11 correctly flags this as the one rejected panel claim. Confirmed correct.
|
|
26
|
+
- **adversarial "`with:` merge/replace semantics undefined"**: §2.2 now states the rule
|
|
27
|
+
explicitly — per-key shallow merge on a step→task→(command|workflow) composition chain,
|
|
28
|
+
referencing step wins, `with.arguments` replaced whole. Checked against §2.3 ("no
|
|
29
|
+
nesting: a task referencing a task is an error") — the chain is capped at depth 1
|
|
30
|
+
exactly where the merge rule assumes it stops, no gap. Checked against §4 ("`with:` is
|
|
31
|
+
not a value field... merges per-key at its target, outside the cascade") — value-field
|
|
32
|
+
cascade (§4's layer list, which includes `uses: tasks/<ref>` as a layer) and `with:`
|
|
33
|
+
merge are two disjoint mechanisms over two disjoint key sets; no overlap, no
|
|
34
|
+
contradiction. Fixed.
|
|
35
|
+
- **adversarial "hashVersion 5 omits persona snapshot"**: §5.3's table now has a
|
|
36
|
+
`persona snapshot hash` row (system prompt, tool policy, value fields). Matches §4.2's
|
|
37
|
+
persona-snapshot shape (`{systemPrompt, toolPolicy, model, …valueFields}`) verbatim —
|
|
38
|
+
same field list on both sides. Fixed, consistent.
|
|
39
|
+
- **adversarial "`.yml` tombstone hard-error has no remedy for read-only bundles"**: §8
|
|
40
|
+
now carves out read-only/third-party bundles to a warning instead of a hard error,
|
|
41
|
+
explicitly citing it as matching the existing migrator precedent. Verified against
|
|
42
|
+
`scripts/akm-migrate/migrate/legacy/task-target-ref-migration.ts:246-266` —
|
|
43
|
+
`planTaskTargetRefMigration` really does skip non-writable bundles and warn-and-continue
|
|
44
|
+
for legacy files it can't rewrite, word-for-word the precedent v9 claims. Fixed.
|
|
45
|
+
> **Editor's note (archival).** The cosmetic stage-name mismatch this
|
|
46
|
+
> report flags in §5.6 was fixed in the same revision that followed this
|
|
47
|
+
> panel: v9 now says the ceiling is evaluated in the **resolve stage** for
|
|
48
|
+
> both surfaces. The finding is retained as the historical record of what
|
|
49
|
+
> the panel saw, not as an open issue.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Majors — verified fixed unless noted
|
|
53
|
+
|
|
54
|
+
All of the following were checked and are substantively addressed with no new
|
|
55
|
+
contradiction: architecture's five majors (task-path staging named in §5.1 as
|
|
56
|
+
"resolve → execute"; `RunnerSpec`-vs-`UnitDispatchRequest` split pushed to the sink
|
|
57
|
+
per §5.1; `ShellUnitExecutor` explicitly extracted from the task runner's hardened path,
|
|
58
|
+
not rebuilt; monotemporal composition rule pins config-snapshot timing for `uses:
|
|
59
|
+
tasks/<ref>`; persona resolver extraction named and costed in §9); adversarial's
|
|
60
|
+
`extra_params` deep-merge preservation (§4 merge-classes, matches `freeze.ts:211-216`
|
|
61
|
+
`mergedLlmOverrides`/`deepMergeConfig` exactly), shell `timeoutMs` slot (§5.2), shell
|
|
62
|
+
crash/lease semantics (§5.2), persona-toolPolicy-vs-engine-capability reconciliation
|
|
63
|
+
(§4.2's "unconsumed-field notice" line folds it into §4 item 4's capability-notice
|
|
64
|
+
mechanism); security's composition-chain bypass (§5.6 bullet 3, "inherit from the
|
|
65
|
+
defining asset's own source"), grant-granularity/bundle-update bypass (moot — grant
|
|
66
|
+
removed), sync-vs-fire-time gap (§5.6 bullet 2, evaluated at resolution not at
|
|
67
|
+
sync/enable time), `env:` literal-secret-heuristic reuse (§5.5, points at
|
|
68
|
+
`param-secrets.ts`'s `detectSecretShapedParams` directly), `AKM_ITEM` shell-injection
|
|
69
|
+
story (§5.5 pins it to the `exec-utils` env-object pattern, never shell-text splice),
|
|
70
|
+
and the two-file migration atomicity gap (§8 states journal-then-verify-then-delete
|
|
71
|
+
ordering explicitly as new work, costed in §9, rather than claiming the existing
|
|
72
|
+
4-artifact `migration-backup.ts` model already covers it — checked, that model really
|
|
73
|
+
is scoped to `config.json`/`state.db`/`workflow.db`/`index.db` only, so v9's honesty
|
|
74
|
+
about this being an extension is accurate, not a claim contradicted by code).
|
|
75
|
+
|
|
76
|
+
- **coverage M13 (improve-exclusion mechanism)**: §3 now removes the `type:"task"`
|
|
77
|
+
pure-YAML special-case (markdown base checks fire) and separately declares tasks
|
|
78
|
+
improve-ineligible via a per-type adapter capability. The capability-declaration
|
|
79
|
+
*pattern* genuinely exists today (`bundle-adapter.ts:9`, "one interface, optional
|
|
80
|
+
capability methods"), so naming that seam is not fabricated — but the concrete
|
|
81
|
+
eligibility check in `src/commands/improve/eligibility.ts` is currently a hardcoded
|
|
82
|
+
`indexed.entry.type === "..."` string comparison, not adapter-capability-driven; v9
|
|
83
|
+
doesn't say eligibility.ts itself gets refactored to consult the new capability
|
|
84
|
+
rather than its own type-string list. Not a contradiction, but thinner than "names a
|
|
85
|
+
mechanism" suggests — residual gap, not blocking.
|
|
86
|
+
- **coverage M9 / adversarial-adjacent (akm-bin shim)**: §5.7's synthesized-shim design
|
|
87
|
+
(write a temp-dir `sh`/`.cmd` shim invoking the exact resolved multi-element
|
|
88
|
+
invocation, PATH-prepend the shim's directory) matches `resolve-akm-bin.ts`'s actual
|
|
89
|
+
return shape (`argv: string[]`, up to two elements for the npm/checkout launcher
|
|
90
|
+
cases) — the shim approach correctly generalizes to the multi-element case a bare PATH
|
|
91
|
+
prepend cannot. Fixed.
|
|
92
|
+
|
|
93
|
+
## Special-attention items — findings
|
|
94
|
+
|
|
95
|
+
1. **§5.6 ceiling enforceability at both resolve points**: the mechanism itself
|
|
96
|
+
(primary-stash-only, checked at the point the target asset is resolved, no override)
|
|
97
|
+
is sound and symmetric for workflows and tasks. However, v9 states the enforcement
|
|
98
|
+
point as "freeze for workflows, dispatch for tasks" — but §5.1 (this same v9 revision)
|
|
99
|
+
explicitly renames task staging to **"resolve → execute"** (no stage named "dispatch"),
|
|
100
|
+
and §5.4 explicitly splits workflow freeze into **compile → resolve → freeze**, with
|
|
101
|
+
asset loading (the thing the ceiling needs to inspect) happening in **resolve**, not
|
|
102
|
+
in the now-narrower **freeze** ("snapshot + hash" only). So §5.6's own enforcement-point
|
|
103
|
+
language doesn't line up with the stage names v9 itself just introduced elsewhere
|
|
104
|
+
(§5.1, §5.4) to fix the architecture panel's "staging is unstated" finding. This is
|
|
105
|
+
cosmetic (both "resolve" and "freeze"/"dispatch" as used here occur before any
|
|
106
|
+
shell/agent actually spawns, so no execution-order security gap results), but it is a
|
|
107
|
+
real terminology inconsistency introduced by the v9 redesign, not present as such
|
|
108
|
+
before the two staging fixes existed to be inconsistent with. Not blocking; a
|
|
109
|
+
copy-edit item.
|
|
110
|
+
2. **"scope: non-AI exec surface only" loophole for third-party `run:` tasks scheduled
|
|
111
|
+
via sync review**: no loophole found. §5.6's own definition of "shell-class work"
|
|
112
|
+
explicitly includes "the `run:` text of a task," so a third-party task whose target is
|
|
113
|
+
`run:` is covered by the ceiling and refuses regardless of whether the operator
|
|
114
|
+
reviewed and enabled it via `sync` (activation-policy rule 3 is a separate, orthogonal
|
|
115
|
+
gate that only concerns *whether the task fires at all*, not *whether its shell target
|
|
116
|
+
is trusted*). The "agent-dispatched third-party tasks... unchanged" carve-out only
|
|
117
|
+
exempts prose/agent-dispatch tasks (which have no direct shell surface here), not
|
|
118
|
+
`run:`/script tasks. No new hole.
|
|
119
|
+
3. **Persona-snapshot hash vs §4.2 resolver extraction**: consistent — §5.3's new row
|
|
120
|
+
lists exactly the fields §4.2's snapshot shape defines (systemPrompt, toolPolicy,
|
|
121
|
+
value fields), and both are attributed to the same extracted resolver.
|
|
122
|
+
4. **`with:` merge rule vs §4 cascade and §2.3's override bullet**: consistent — verified
|
|
123
|
+
above, disjoint mechanisms, no double-counting.
|
|
124
|
+
5. **Tombstone carve-out + crash-window ordering vs §8's journal claims and the real
|
|
125
|
+
migrator**: consistent — the read-only carve-out matches
|
|
126
|
+
`task-target-ref-migration.ts`'s existing warn-and-skip behavior; the create-verify-
|
|
127
|
+
delete ordering is honestly presented as new work extending (not already covered by)
|
|
128
|
+
`migration-backup.ts`'s 4-artifact model, and it's costed in §9's "Named refactors" row.
|
|
129
|
+
6. **Shim design vs `resolve-akm-bin.ts`**: consistent — the shim generalizes correctly
|
|
130
|
+
to the multi-element launcher case `resolveAkmInvocation()` can return.
|
|
131
|
+
|
|
132
|
+
## Verdict
|
|
133
|
+
|
|
134
|
+
ALL FIXES HOLD.
|
|
135
|
+
|
|
136
|
+
Residual (non-blocking) notes:
|
|
137
|
+
- §5.6's "freeze for workflows, dispatch for tasks" enforcement-point language uses stage
|
|
138
|
+
names that don't match the stage vocabulary §5.1/§5.4 just introduced in this same
|
|
139
|
+
revision ("resolve → execute" for tasks; compile→resolve→freeze for workflows, with
|
|
140
|
+
asset resolution living in "resolve" not "freeze"). Suggest wording as "resolve for
|
|
141
|
+
workflows, resolve for tasks" (or just "at resolve, for both") to align with §5.1/§5.4.
|
|
142
|
+
- §3's improve-ineligibility fix correctly names an existing capability-declaration
|
|
143
|
+
*pattern* in the adapter interface, but doesn't state that
|
|
144
|
+
`src/commands/improve/eligibility.ts`'s current hardcoded `entry.type === "..."` checks
|
|
145
|
+
need to be rewired to consult that capability — worth a one-line addition if precision
|
|
146
|
+
matters, but doesn't reopen the original finding (a mechanism is now named, whereas
|
|
147
|
+
before none was).
|