akm-cli 0.9.0 → 0.9.1-beta.2

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.
Files changed (140) hide show
  1. package/CHANGELOG.md +724 -0
  2. package/README.md +28 -63
  3. package/STABILITY.md +4 -2
  4. package/dist/cli/parse-args.js +7 -1
  5. package/dist/commands/agent/contribute-cli.js +1 -1
  6. package/dist/commands/env/child-env.js +14 -0
  7. package/dist/commands/feedback-cli.js +7 -1
  8. package/dist/commands/health/llm-usage.js +2 -1
  9. package/dist/commands/health/surfaces.js +4 -77
  10. package/dist/commands/health.js +65 -11
  11. package/dist/commands/improve/distill/quality-gate.js +6 -1
  12. package/dist/commands/improve/eligibility.js +7 -1
  13. package/dist/commands/improve/eval-cases.js +2 -0
  14. package/dist/commands/improve/improve.js +126 -10
  15. package/dist/commands/improve/locks.js +7 -0
  16. package/dist/commands/improve/memory/memory-improve.js +9 -0
  17. package/dist/commands/improve/run-context.js +5 -0
  18. package/dist/commands/improve/session-asset.js +4 -0
  19. package/dist/commands/lint/base-linter.js +31 -7
  20. package/dist/commands/lint/index.js +205 -51
  21. package/dist/commands/lint/types.js +22 -1
  22. package/dist/commands/proposal/repository.js +17 -1
  23. package/dist/commands/sources/add-cli.js +8 -2
  24. package/dist/commands/sources/info.js +12 -2
  25. package/dist/commands/sources/installed-stashes.js +6 -1
  26. package/dist/commands/sources/migration-help.js +12 -3
  27. package/dist/commands/sources/self-update.js +9 -1
  28. package/dist/commands/tasks/tasks.js +8 -2
  29. package/dist/commands/workflow-cli.js +17 -11
  30. package/dist/core/abort-deadline.js +28 -0
  31. package/dist/core/adapter/adapters/agent-skills-adapter.js +83 -5
  32. package/dist/core/adapter/adapters/akm-adapter.js +13 -10
  33. package/dist/core/adapter/adapters/akm-lint.js +78 -22
  34. package/dist/core/adapter/adapters/akm-task-adapter.js +43 -20
  35. package/dist/core/adapter/adapters/dotenv-adapter.js +21 -0
  36. package/dist/core/adapter/adapters/tool-dir-shared.js +5 -3
  37. package/dist/core/asset/frontmatter.js +10 -1
  38. package/dist/core/common.js +147 -9
  39. package/dist/core/concurrent.js +32 -0
  40. package/dist/core/config/config-io.js +5 -45
  41. package/dist/core/config/schema/engines.js +14 -3
  42. package/dist/core/config/schema/workflow.js +11 -0
  43. package/dist/core/errors.js +25 -0
  44. package/dist/core/events.js +30 -24
  45. package/dist/core/extra-params.js +11 -0
  46. package/dist/core/file-lock.js +7 -1
  47. package/dist/core/fs-txn.js +15 -2
  48. package/dist/core/improve-result.js +5 -0
  49. package/dist/core/json-schema.js +344 -9
  50. package/dist/core/loopback.js +89 -0
  51. package/dist/core/migration-operation.js +17 -2
  52. package/dist/core/path-access.js +107 -0
  53. package/dist/core/paths.js +16 -2
  54. package/dist/core/redaction.js +86 -18
  55. package/dist/core/spawn-env.js +234 -0
  56. package/dist/core/state-db-scope.js +134 -0
  57. package/dist/core/state-db.js +1 -0
  58. package/dist/core/subprocess.js +181 -37
  59. package/dist/core/write-provenance.js +85 -0
  60. package/dist/core/write-source.js +33 -2
  61. package/dist/indexer/db/graph-db.js +17 -6
  62. package/dist/indexer/ensure-index.js +10 -3
  63. package/dist/indexer/index-written-assets.js +17 -2
  64. package/dist/indexer/indexer.js +86 -21
  65. package/dist/indexer/passes/memory-inference.js +4 -0
  66. package/dist/indexer/search/db-search.js +25 -17
  67. package/dist/indexer/walk/walker.js +6 -1
  68. package/dist/integrations/agent/detect.js +13 -1
  69. package/dist/integrations/agent/engine-resolution.js +24 -11
  70. package/dist/integrations/agent/model-aliases.js +1 -1
  71. package/dist/integrations/agent/profiles.js +9 -1
  72. package/dist/integrations/agent/spawn.js +15 -87
  73. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +21 -0
  74. package/dist/integrations/lockfile.js +55 -2
  75. package/dist/llm/client.js +14 -19
  76. package/dist/llm/embedder.js +23 -3
  77. package/dist/llm/embedders/remote.js +27 -2
  78. package/dist/output/html-render.js +40 -1
  79. package/dist/output/text/lint-format.js +17 -4
  80. package/dist/runtime.js +23 -1
  81. package/dist/scripts/akm-migrate-node.js +1714 -836
  82. package/dist/scripts/akm-migrate.js +1682 -804
  83. package/dist/setup/setup.js +22 -7
  84. package/dist/sources/providers/git-install.js +25 -2
  85. package/dist/sources/providers/git-stash.js +19 -0
  86. package/dist/sources/providers/git.js +1 -1
  87. package/dist/sources/snapshot-fetchers/content-extract.js +63 -1
  88. package/dist/sources/snapshot-fetchers/website-ingest.js +126 -20
  89. package/dist/storage/database.js +71 -7
  90. package/dist/storage/engines/sqlite-migrations.js +61 -2
  91. package/dist/storage/managed-db.js +19 -0
  92. package/dist/storage/repositories/index-connection.js +39 -4
  93. package/dist/storage/repositories/index-entries-repository.js +6 -1
  94. package/dist/storage/repositories/index-meta-repository.js +11 -0
  95. package/dist/storage/repositories/index-schema.js +17 -2
  96. package/dist/storage/repositories/index-vec-repository.js +43 -5
  97. package/dist/storage/repositories/workflow-runs-repository.js +66 -13
  98. package/dist/storage/sqlite-pragmas.js +12 -1
  99. package/dist/tasks/log-redaction.js +156 -0
  100. package/dist/tasks/parser.js +82 -5
  101. package/dist/tasks/runner.js +222 -17
  102. package/dist/tasks/scheduler-invocation.js +19 -0
  103. package/dist/tasks/schema.js +86 -1
  104. package/dist/text-import-hook.mjs +1 -1
  105. package/dist/workflows/concurrency-policy.js +95 -1
  106. package/dist/workflows/exec/dispatch-redaction.js +114 -0
  107. package/dist/workflows/exec/exec-unit.js +542 -0
  108. package/dist/workflows/exec/frozen-judge.js +114 -42
  109. package/dist/workflows/exec/native-executor.js +465 -238
  110. package/dist/workflows/exec/param-secrets.js +4 -3
  111. package/dist/workflows/exec/run-workflow.js +424 -219
  112. package/dist/workflows/exec/step-work.js +506 -167
  113. package/dist/workflows/exec/unit-dispatch.js +31 -1
  114. package/dist/workflows/exec/unit-writer.js +53 -13
  115. package/dist/workflows/exec/worktree.js +454 -41
  116. package/dist/workflows/ir/compile.js +26 -2
  117. package/dist/workflows/ir/freeze.js +82 -15
  118. package/dist/workflows/ir/schema.js +105 -20
  119. package/dist/workflows/parser.js +242 -19
  120. package/dist/workflows/program/schema.js +24 -0
  121. package/dist/workflows/renderer.js +32 -4
  122. package/dist/workflows/resource-limits.js +182 -0
  123. package/dist/workflows/runtime/runs.js +146 -6
  124. package/dist/workflows/validate-summary.js +17 -2
  125. package/docs/README.md +74 -32
  126. package/docs/migration/release-notes/0.9.0.md +2 -1
  127. package/docs/migration/v0.7-to-v0.8.md +2 -1
  128. package/docs/migration/v0.8-to-v0.9.md +3 -1
  129. package/docs/reference/README.md +11 -4
  130. package/docs/reference/bundle-types.md +19 -0
  131. package/docs/reference/cli.md +105 -16
  132. package/docs/reference/configuration.md +15 -2
  133. package/docs/reference/data-and-telemetry.md +30 -10
  134. package/docs/reference/supported-formats.md +50 -0
  135. package/docs/reference/workflow-schema.md +1014 -0
  136. package/docs/reference/workflows.md +37 -633
  137. package/package.json +13 -6
  138. package/schemas/akm-config.json +18 -5
  139. package/schemas/akm-task.json +27 -5
  140. package/schemas/akm-workflow.json +92 -13
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.0",
3
+ "version": "0.9.1-beta.2",
4
4
  "type": "module",
5
- "description": "akm (Agent Knowledge Manager) — A knowledge toolkit for AI agents: capture, curate, search, and share skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
5
+ "description": "akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cursor.",
6
6
  "keywords": [
7
7
  "akm",
8
8
  "agent-knowledge-management",
@@ -21,7 +21,9 @@
21
21
  "mcp",
22
22
  "ai-coding-assistant",
23
23
  "agent-skills",
24
- "skill-management"
24
+ "skill-management",
25
+ "capability-library",
26
+ "agent-capabilities"
25
27
  ],
26
28
  "homepage": "https://github.com/itlackey/akm#readme",
27
29
  "repository": {
@@ -33,7 +35,8 @@
33
35
  },
34
36
  "license": "MPL-2.0",
35
37
  "pinNotes": {
36
- "@opencode-ai/sdk@1.2.20": "Exact pin. The SDK surface we use (createOpencodeClient + session.create/prompt/delete in src/integrations/harnesses/opencode-sdk/sdk-runner.ts) is stable across 1.x, but the SDK has shipped 5+ minor versions of unrelated provider/registry churn. akm-cli is a global CLI install so the pin is isolated from user-project deps. Re-test sdk-runner before bumping. Note this package is an HTTP client only — it declares no dependencies and its own createOpencodeServer spawns `opencode serve` — so it does NOT make the opencode binary available; that install is separate and is what every SDK-path probe checks for."
38
+ "@opencode-ai/sdk@1.2.20": "Exact pin. The SDK surface we use (createOpencodeClient + session.create/prompt/delete in src/integrations/harnesses/opencode-sdk/sdk-runner.ts) is stable across 1.x, but the SDK has shipped 5+ minor versions of unrelated provider/registry churn. akm-cli is a global CLI install so the pin is isolated from user-project deps. Re-test sdk-runner before bumping. Note this package is an HTTP client only — it declares no dependencies and its own createOpencodeServer spawns `opencode serve` — so it does NOT make the opencode binary available; that install is separate and is what every SDK-path probe checks for.",
39
+ "better-sqlite3@12.11.1": "Exact pin (#790). This is the SQLite driver akm loads on Node (src/storage/database.ts); Bun never touches it. The pin is about PREBUILT BINARIES, not API surface. better-sqlite3 ships one prebuild per Node ABI as a GitHub release asset and its install script is `prebuild-install || node-gyp rebuild` — so any (version, Node ABI) pair with no prebuild silently COMPILES FROM SOURCE against the headers of whatever Node is on the machine that day. The 11.x line predates Node 24 and declares no `engines` at all: its newest release (11.10.0, 2025-05-08) publishes ABI 108/115/127/131 (Node 18/20/22/23) and nothing for ABI 137 (Node 24). Node 24.19.0 then changed the public `node_object_wrap.h` so `node::ObjectWrap`'s ctor/dtor register and unregister an environment cleanup hook; a from-source 11.x build against those headers aborts at teardown in `Statement::~Statement()` with `RemoveEnvironmentCleanupHook ... Assertion (env) != nullptr`, intermittently, depending on GC timing. 12.11.1 publishes ABI 127/137/141/147 (Node 22/24/25/26) and declares `engines: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x`, so every Node akm supports installs a prebuilt binary and never compiles. Before bumping: confirm the target version publishes a prebuild for EVERY Node major in `engines` (probe https://github.com/WiseLibs/better-sqlite3/releases/download/vX.Y.Z/better-sqlite3-vX.Y.Z-node-vABI-linux-x64.tar.gz), not just that the version is newer. 13.x is the eventual destination — it moved to node-addon-api/N-API with prebuilds bundled in the npm tarball and no install script, which retires this failure mode entirely — but it is a fresh major rewrite of the binding, so it wants its own soak, not a patch release. The CI node-smoke job installs this exact string by reading it back out of this file (.github/workflows/ci.yml), so the two cannot drift."
37
40
  },
38
41
  "files": [
39
42
  "dist",
@@ -46,9 +49,12 @@
46
49
  "docs/migration/v0.7-to-v0.8.md",
47
50
  "docs/migration/v0.8-to-v0.9.md",
48
51
  "docs/migration/v0.9.0-troubleshooting.md",
52
+ "docs/reference/bundle-types.md",
49
53
  "docs/reference/cli.md",
50
54
  "docs/reference/configuration.md",
51
55
  "docs/reference/data-and-telemetry.md",
56
+ "docs/reference/supported-formats.md",
57
+ "docs/reference/workflow-schema.md",
52
58
  "docs/reference/workflows.md",
53
59
  "schemas"
54
60
  ],
@@ -71,11 +77,12 @@
71
77
  "test:time": "bun scripts/test-timing-report.ts",
72
78
  "lint:isolation": "bun scripts/lint-tests-isolation.ts",
73
79
  "lint:doc-examples": "bun scripts/lint-doc-examples.ts",
80
+ "lint:active-docs-terminology": "bun scripts/lint-active-docs-terminology.ts",
74
81
  "lint:devto-posts": "bun scripts/lint-devto-posts.ts",
75
82
  "lint:devto-posts:fix": "bun scripts/lint-devto-posts.ts --fix",
76
83
  "publish:devto": "npx -y @sinedied/devto-cli push \"docs/posts/**/*.md\" --token \"$DEVTO_TOKEN\" --repo \"$GITHUB_REPOSITORY\" --branch \"${GITHUB_REF_NAME:-main}\" --reconcile",
77
84
  "release:check": "./tests/release-check.sh",
78
- "lint": "bunx biome check src/ tests/ scripts/ && bun scripts/lint-tests-isolation.ts && bun scripts/lint-license-headers.ts && bun scripts/lint-runtime-boundary.ts && bun scripts/lint-write-source-chokepoint.ts && bun scripts/lint-secret-resolver-boundary.ts && bun scripts/lint-process-argv.ts && bun scripts/lint-repository-sql.ts && bun scripts/lint-goldens-presence.ts && bun scripts/lint-golden-captured-at-head.ts && bun scripts/lint-test-ref-literals.ts && bun scripts/lint-shipped-assets.ts && bun scripts/lint-doc-examples.ts && bun scripts/gen-config-schema.ts --check",
85
+ "lint": "bunx biome check src/ tests/ scripts/ && bun scripts/lint-tests-isolation.ts && bun scripts/lint-license-headers.ts && bun scripts/lint-runtime-boundary.ts && bun scripts/lint-write-source-chokepoint.ts && bun scripts/lint-secret-resolver-boundary.ts && bun scripts/lint-process-argv.ts && bun scripts/lint-repository-sql.ts && bun scripts/lint-goldens-presence.ts && bun scripts/lint-golden-captured-at-head.ts && bun scripts/lint-test-ref-literals.ts && bun scripts/lint-shipped-assets.ts && bun scripts/lint-doc-examples.ts && bun scripts/gen-config-schema.ts --check && bun scripts/lint-active-docs-terminology.ts",
79
86
  "lint:runtime-boundary": "bun scripts/lint-runtime-boundary.ts",
80
87
  "lint:tests-isolation": "bun scripts/lint-tests-isolation.ts",
81
88
  "lint:fix": "bunx biome check --write src/ tests/ scripts/",
@@ -100,7 +107,7 @@
100
107
  },
101
108
  "optionalDependencies": {
102
109
  "@huggingface/transformers": "^4.2.0",
103
- "better-sqlite3": "^11.8.0",
110
+ "better-sqlite3": "12.11.1",
104
111
  "sqlite-vec": "^0.1.9"
105
112
  },
106
113
  "engines": {
@@ -45,7 +45,8 @@
45
45
  "anyOf": [
46
46
  {
47
47
  "type": "integer",
48
- "exclusiveMinimum": 0
48
+ "exclusiveMinimum": 0,
49
+ "maximum": 2147483647
49
50
  },
50
51
  {
51
52
  "type": "null"
@@ -122,7 +123,8 @@
122
123
  "anyOf": [
123
124
  {
124
125
  "type": "integer",
125
- "exclusiveMinimum": 0
126
+ "exclusiveMinimum": 0,
127
+ "maximum": 2147483647
126
128
  },
127
129
  {
128
130
  "type": "null"
@@ -1622,6 +1624,10 @@
1622
1624
  "type": "integer",
1623
1625
  "exclusiveMinimum": 0
1624
1626
  },
1627
+ "defaultMapConcurrency": {
1628
+ "type": "integer",
1629
+ "exclusiveMinimum": 0
1630
+ },
1625
1631
  "judgeEngine": {
1626
1632
  "type": "string",
1627
1633
  "maxLength": 63,
@@ -1693,7 +1699,8 @@
1693
1699
  "anyOf": [
1694
1700
  {
1695
1701
  "type": "integer",
1696
- "exclusiveMinimum": 0
1702
+ "exclusiveMinimum": 0,
1703
+ "maximum": 2147483647
1697
1704
  },
1698
1705
  {
1699
1706
  "type": "null"
@@ -1770,7 +1777,8 @@
1770
1777
  "anyOf": [
1771
1778
  {
1772
1779
  "type": "integer",
1773
- "exclusiveMinimum": 0
1780
+ "exclusiveMinimum": 0,
1781
+ "maximum": 2147483647
1774
1782
  },
1775
1783
  {
1776
1784
  "type": "null"
@@ -3270,6 +3278,10 @@
3270
3278
  "type": "integer",
3271
3279
  "exclusiveMinimum": 0
3272
3280
  },
3281
+ "defaultMapConcurrency": {
3282
+ "type": "integer",
3283
+ "exclusiveMinimum": 0
3284
+ },
3273
3285
  "judgeEngine": {
3274
3286
  "type": "string",
3275
3287
  "maxLength": 63,
@@ -3324,7 +3336,8 @@
3324
3336
  "anyOf": [
3325
3337
  {
3326
3338
  "type": "integer",
3327
- "exclusiveMinimum": 0
3339
+ "exclusiveMinimum": 0,
3340
+ "maximum": 2147483647
3328
3341
  },
3329
3342
  {
3330
3343
  "type": "null"
@@ -25,21 +25,43 @@
25
25
  "params": { "type": "object" },
26
26
  "engine": { "type": "string", "minLength": 1 },
27
27
  "model": { "type": "string", "minLength": 1 },
28
- "timeoutMs": { "type": ["integer", "null"], "minimum": 1 },
29
- "llm": { "$ref": "#/definitions/llm" }
28
+ "timeoutMs": {
29
+ "type": ["integer", "null"],
30
+ "minimum": 1,
31
+ "maximum": 2147483647,
32
+ "description": "Timeout in milliseconds, or null to disable it. Prompt tasks: the engine timeout. Command tasks: the subprocess timeout. Workflow tasks: the whole-run timeout — omit it to take the unattended default (DEFAULT_WORKFLOW_TASK_TIMEOUT_MS in src/tasks/runner.ts), null to run unbounded."
33
+ },
34
+ "maxSteps": {
35
+ "type": "integer",
36
+ "minimum": 1,
37
+ "description": "Workflow tasks only: stop after executing this many workflow steps (`akm workflow run --max-steps`)."
38
+ },
39
+ "maxRetries": {
40
+ "type": "integer",
41
+ "minimum": 0,
42
+ "maximum": 100,
43
+ "description": "Workflow tasks only: retry a failed workflow step this many additional times (`akm workflow run --max-retries`)."
44
+ },
45
+ "llm": { "$ref": "#/definitions/llm" },
46
+ "redact": {
47
+ "type": "array",
48
+ "maxItems": 32,
49
+ "items": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
50
+ "description": "NAMES ONLY: environment variable names whose values are scrubbed from this task's persisted run log and logs.db rows. Values never appear in a task file — a task file is indexed, searchable, and printed verbatim by `akm show`, so a literal secret here would leak more widely than the redaction closes. akm already scrubs config-declared credentials and infers others from the variable name; this is the escape hatch for a secret exported under a name none of those rules recognise. A name unset at run time contributes nothing."
51
+ }
30
52
  },
31
53
  "oneOf": [
32
54
  {
33
55
  "required": ["workflow"],
34
- "not": { "anyOf": [{ "required": ["prompt"] }, { "required": ["command"] }, { "required": ["engine"] }, { "required": ["model"] }, { "required": ["timeoutMs"] }, { "required": ["llm"] }] }
56
+ "not": { "anyOf": [{ "required": ["prompt"] }, { "required": ["command"] }, { "required": ["engine"] }, { "required": ["model"] }, { "required": ["llm"] }] }
35
57
  },
36
58
  {
37
59
  "required": ["prompt"],
38
- "not": { "anyOf": [{ "required": ["workflow"] }, { "required": ["command"] }, { "required": ["params"] }] }
60
+ "not": { "anyOf": [{ "required": ["workflow"] }, { "required": ["command"] }, { "required": ["params"] }, { "required": ["maxSteps"] }, { "required": ["maxRetries"] }] }
39
61
  },
40
62
  {
41
63
  "required": ["command"],
42
- "not": { "anyOf": [{ "required": ["workflow"] }, { "required": ["prompt"] }, { "required": ["params"] }, { "required": ["engine"] }, { "required": ["model"] }, { "required": ["llm"] }] }
64
+ "not": { "anyOf": [{ "required": ["workflow"] }, { "required": ["prompt"] }, { "required": ["params"] }, { "required": ["engine"] }, { "required": ["model"] }, { "required": ["llm"] }, { "required": ["maxSteps"] }, { "required": ["maxRetries"] }] }
43
65
  }
44
66
  ],
45
67
  "definitions": {
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://itlackey.github.io/akm/schemas/akm-workflow.json",
4
4
  "title": "AKM Workflow",
5
- "description": "Unified akm workflow frontmatter (workflow-format-unification). One markdown asset: the standard AKM asset envelope (type/description/tags/when_to_use/xrefs/updated/timestamp + the OKF v0.2 machine-stamped families) plus the orchestration graph. Per-step prose instructions and gate rubrics live in the markdown body, joined to this graph by step id — see docs/reference/workflows.md. Hand-authored alongside src/workflows/program/schema.ts; the enum vocabularies and patterns here are pinned against the TypeScript constants by tests/integration/workflows/schema-drift.test.ts.",
5
+ "description": "Unified akm workflow frontmatter (workflow-format-unification). One markdown asset: the standard AKM asset envelope (type/description/tags/when_to_use/xrefs/updated/timestamp + the OKF v0.2 machine-stamped families) plus the orchestration graph. Per-step prose instructions and gate rubrics live in the markdown body, joined to this graph by step id — see docs/reference/workflow-schema.md. Hand-authored alongside src/workflows/program/schema.ts; the enum vocabularies and patterns here are pinned against the TypeScript constants by tests/integration/workflows/schema-drift.test.ts.",
6
6
  "type": "object",
7
7
  "required": ["steps"],
8
8
  "additionalProperties": false,
@@ -57,7 +57,7 @@
57
57
  "description": "A bare reference string: params.<name> or steps.<id>.output(.<ident>|[<int>])*. No ${{ }} delimiters — every position this appears in is a whole-value frontmatter field. Full grammar validation happens at compile time, not here."
58
58
  },
59
59
  "timeout": {
60
- "description": "Duration: \"<n>ms\" | \"<n>s\" | \"<n>m\" | \"none\" (bare integers are milliseconds).",
60
+ "description": "Duration: \"<n>ms\" | \"<n>s\" | \"<n>m\" | \"none\" (bare integers are milliseconds). Must resolve to at most 2147483647 ms (setTimeout's 32-bit signed ceiling) — the parser enforces the same ceiling on the string form.",
61
61
  "oneOf": [
62
62
  {
63
63
  "type": "string",
@@ -65,10 +65,18 @@
65
65
  },
66
66
  {
67
67
  "type": "integer",
68
- "minimum": 1
68
+ "minimum": 1,
69
+ "maximum": 2147483647
69
70
  }
70
71
  ]
71
72
  },
73
+ "engineName": {
74
+ "type": "string",
75
+ "minLength": 1,
76
+ "maxLength": 63,
77
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
78
+ "description": "Named engine: lowercase dash-separated words of letters and digits, starting with a letter. Same grammar the frozen-plan decoder enforces (src/workflows/resource-limits.ts WORKFLOW_ENGINE_NAME_PATTERN)."
79
+ },
72
80
  "onError": {
73
81
  "description": "Failure policy: fail the step on the first unit failure (default), or record failures and let the gate decide.",
74
82
  "enum": ["fail", "continue"]
@@ -140,7 +148,7 @@
140
148
  },
141
149
  "jsonSchemaObject": {
142
150
  "type": "object",
143
- "description": "A JSON Schema declaration (validated as a schema by the compiler, not here)."
151
+ "description": "A JSON Schema declaration. The parser validates it as a schema definition against the runtime's enforced subset (src/core/json-schema.ts checkJsonSchemaDefinition): typo'd type names and keywords outside the subset (e.g. $ref, const, format, patternProperties) are authoring-time errors, not silent no-ops."
144
152
  },
145
153
  "retry": {
146
154
  "type": "object",
@@ -151,6 +159,7 @@
151
159
  "max": {
152
160
  "type": "integer",
153
161
  "minimum": 0,
162
+ "maximum": 100,
154
163
  "description": "Maximum retry attempts per unit."
155
164
  },
156
165
  "on": {
@@ -186,8 +195,7 @@
186
195
  "additionalProperties": false,
187
196
  "properties": {
188
197
  "engine": {
189
- "type": "string",
190
- "minLength": 1
198
+ "$ref": "#/definitions/engineName"
191
199
  },
192
200
  "model": {
193
201
  "type": "string",
@@ -205,14 +213,79 @@
205
213
  }
206
214
  }
207
215
  },
208
- "unit": {
216
+ "exec": {
209
217
  "type": "object",
210
- "description": "Optional dispatch-override bag for a unit/map step. A step with none of these carries the run's engine/model/timeout defaults verbatim.",
218
+ "description": "Run a shell command as this unit instead of dispatching to an engine. `command` is an ARGV ARRAY and there is no shell-string form: the child is spawned directly, so shell metacharacters (`;` `|` `&&` `$(…)` `>` `*`) are inert literal argument bytes — the quoting/injection class simply does not exist. Write the interpreter explicitly (e.g. [\"bash\", \"-lc\", \"a | b\"]) when a pipeline is really wanted. stdout is the promoted step artifact with trailing newlines stripped (like shell `$(…)`); when the unit declares an `output` schema, stdout must instead be exactly one JSON value, which is validated against it. A non-zero exit fails the unit with failure reason `non_zero_exit`, honoring `retry:` and `on_error:` like any other unit. The child's environment is an ALLOWLIST by default (PATH, HOME, locale/temp vars, the Windows process-creation essentials) — widen it with `pass_env` or `inherit_env`.",
219
+ "required": ["command"],
211
220
  "additionalProperties": false,
212
221
  "properties": {
213
- "engine": {
222
+ "command": {
223
+ "type": "array",
224
+ "description": "argv; command[0] is the program, resolved through PATH. Never shell-parsed.",
225
+ "minItems": 1,
226
+ "maxItems": 64,
227
+ "items": {
228
+ "type": "string",
229
+ "minLength": 1,
230
+ "maxLength": 4096
231
+ }
232
+ },
233
+ "cwd": {
214
234
  "type": "string",
235
+ "description": "Optional RELATIVE working directory inside the unit's working directory (its fresh worktree under `isolation: worktree`). Absolute paths, Windows drive letters, \"~\", and \"..\" segments are rejected here, and containment is re-checked against the resolved base — symlinks included — before the command is spawned.",
215
236
  "minLength": 1,
237
+ "maxLength": 1024,
238
+ "pattern": "^(?!/|\\\\|~|[A-Za-z]:)(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$"
239
+ },
240
+ "pass_env": {
241
+ "type": "array",
242
+ "description": "Extra environment variable NAMES copied through from akm's own environment on top of the default allowlist — for a per-machine toolchain variable (e.g. CARGO_HOME) that no committed `env:` asset could carry. NAMES ONLY: values never appear in a workflow, and unlike `env:` bindings these values are NOT redacted from the command's output, so never list a credential here.",
243
+ "minItems": 1,
244
+ "maxItems": 32,
245
+ "uniqueItems": true,
246
+ "items": {
247
+ "type": "string",
248
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
249
+ }
250
+ },
251
+ "inherit_env": {
252
+ "type": "boolean",
253
+ "description": "Give the command akm's ENTIRE environment instead of the default allowlist. Opt in only when a command genuinely needs the caller's whole environment (a wrapper script, a toolchain with many ambient variables); prefer `pass_env` or `env:` bindings, which keep what the command can see explicit in the frontmatter diff. Dispatch-significant: toggling it changes the unit's input hash."
254
+ }
255
+ }
256
+ },
257
+ "unit": {
258
+ "type": "object",
259
+ "description": "Optional dispatch-override bag for a unit/map step. A step with none of these carries the run's engine/model/timeout defaults verbatim. Declaring `exec` turns the step into a shell-command unit: it names no engine, so `engine`, `model`, and `llm` are rejected alongside it.",
260
+ "additionalProperties": false,
261
+ "allOf": [
262
+ {
263
+ "if": {
264
+ "required": ["exec"]
265
+ },
266
+ "then": {
267
+ "not": {
268
+ "anyOf": [
269
+ {
270
+ "required": ["engine"]
271
+ },
272
+ {
273
+ "required": ["model"]
274
+ },
275
+ {
276
+ "required": ["llm"]
277
+ }
278
+ ]
279
+ }
280
+ }
281
+ }
282
+ ],
283
+ "properties": {
284
+ "exec": {
285
+ "$ref": "#/definitions/exec"
286
+ },
287
+ "engine": {
288
+ "$ref": "#/definitions/engineName",
216
289
  "description": "Named engine selected when the workflow run is created."
217
290
  },
218
291
  "model": {
@@ -236,11 +309,12 @@
236
309
  },
237
310
  "env": {
238
311
  "type": "array",
312
+ "uniqueItems": true,
239
313
  "items": {
240
314
  "type": "string",
241
315
  "minLength": 1
242
316
  },
243
- "description": "Env asset refs injected into the dispatched unit env."
317
+ "description": "Env asset refs injected into the dispatched unit env. Entries must be unique: the frozen-plan decoder rejects duplicates at run start."
244
318
  },
245
319
  "isolation": {
246
320
  "$ref": "#/definitions/isolation"
@@ -260,7 +334,8 @@
260
334
  "concurrency": {
261
335
  "type": "integer",
262
336
  "minimum": 1,
263
- "description": "Max concurrent units for this step; capped by the engine's global limit."
337
+ "maximum": 64,
338
+ "description": "Max concurrent units for this step. Defaults to workflow.defaultMapConcurrency (4 when unset) — a map step is parallel by default since 0.9.1; write 1 for serial execution. The effective width is the minimum of this, workflow.maxConcurrency, the selected engine's concurrency, and the host CPU cap."
264
339
  },
265
340
  "reducer": {
266
341
  "$ref": "#/definitions/reducer"
@@ -276,7 +351,9 @@
276
351
  "additionalProperties": false,
277
352
  "properties": {
278
353
  "match": {
279
- "type": ["string", "number", "boolean"]
354
+ "type": ["string", "number", "boolean"],
355
+ "minLength": 1,
356
+ "description": "Branch key. An empty string is rejected: the frozen-plan decoder requires every `when` key to be non-empty."
280
357
  },
281
358
  "step": {
282
359
  "$ref": "#/definitions/identifier"
@@ -314,6 +391,7 @@
314
391
  "max_loops": {
315
392
  "type": "integer",
316
393
  "minimum": 1,
394
+ "maximum": 100,
317
395
  "description": "Evaluator-optimizer loop bound."
318
396
  }
319
397
  }
@@ -322,10 +400,11 @@
322
400
  "type": "array",
323
401
  "minItems": 1,
324
402
  "maxItems": 64,
403
+ "uniqueItems": true,
325
404
  "items": {
326
405
  "$ref": "#/definitions/reference"
327
406
  },
328
- "description": "Prior-step artifacts this unit/map step consumes, as reference strings (sub-paths legal). Attached to the dispatched unit as structured context; never spliced into instructions."
407
+ "description": "Prior-step artifacts this unit/map step consumes, as reference strings (sub-paths legal). Attached to the dispatched unit as structured context; never spliced into instructions. Entries must be unique: the frozen-plan decoder rejects duplicates at run start."
329
408
  },
330
409
  "step": {
331
410
  "type": "object",