@open-agent-toolkit/cli 0.2.66 → 0.2.68

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 (118) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/configuration.md +14 -1
  3. package/assets/docs/cli-utilities/project-log.md +63 -4
  4. package/assets/docs/contributing/code.md +8 -4
  5. package/assets/docs/contributing/skills.md +29 -17
  6. package/assets/docs/docs-tooling/commands.md +3 -1
  7. package/assets/docs/provider-sync/manifest-and-drift.md +2 -18
  8. package/assets/docs/provider-sync/scope-and-surface.md +3 -2
  9. package/assets/docs/reference/cli-reference.md +1 -1
  10. package/assets/docs/workflows/projects/evidence-layers.md +6 -3
  11. package/assets/docs/workflows/projects/implementation-execution.md +4 -3
  12. package/assets/docs/workflows/projects/lifecycle.md +17 -0
  13. package/assets/docs/workflows/projects/orchestration-model.md +6 -4
  14. package/assets/docs/workflows/projects/picking-up-projects.md +18 -0
  15. package/assets/docs/workflows/skills/repo-improve.md +7 -3
  16. package/assets/public-package-versions.json +4 -4
  17. package/assets/skills/create-agnostic-skill/SKILL.md +19 -37
  18. package/assets/skills/create-agnostic-skill/references/docs/skills-guide.md +58 -15
  19. package/assets/skills/create-agnostic-skill/references/skill-template.md +17 -15
  20. package/assets/skills/oat-agent-instructions-apply/SKILL.md +1 -1
  21. package/assets/skills/oat-agent-instructions-apply/references/instruction-file-templates/glob-scoped-rule.md +1 -4
  22. package/assets/skills/oat-dispatch-subagents/SKILL.md +4 -2
  23. package/assets/skills/oat-doctor/SKILL.md +1 -4
  24. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +3 -2
  25. package/assets/skills/oat-project-complete/SKILL.md +158 -30
  26. package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +26 -7
  27. package/assets/skills/oat-project-complete/scripts/validate-durable-archive-receipt.mjs +183 -0
  28. package/assets/skills/oat-project-dispatch-subagents/SKILL.md +9 -7
  29. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +3 -2
  30. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  31. package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +11 -10
  32. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +3 -2
  33. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +3 -2
  34. package/assets/skills/oat-project-plan-writing/SKILL.md +7 -6
  35. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +3 -2
  36. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +3 -2
  37. package/assets/skills/oat-project-retro/SKILL.md +1 -1
  38. package/assets/skills/oat-project-retro/references/apply-procedure.md +11 -0
  39. package/assets/skills/oat-project-review-provide/SKILL.md +14 -13
  40. package/assets/skills/oat-project-review-provide-remote/SKILL.md +7 -6
  41. package/assets/skills/oat-project-summary/SKILL.md +24 -2
  42. package/assets/skills/oat-repo-improve/SKILL.md +9 -2
  43. package/assets/skills/oat-repo-improve/references/plan-template.md +4 -6
  44. package/assets/skills/oat-repo-knowledge-index/SKILL.md +11 -9
  45. package/assets/skills/oat-wave-execute/SKILL.md +20 -6
  46. package/assets/skills/oat-wave-program/SKILL.md +9 -4
  47. package/dist/commands/config/index.d.ts +2 -1
  48. package/dist/commands/config/index.d.ts.map +1 -1
  49. package/dist/commands/config/index.js +133 -38
  50. package/dist/commands/docs/index-generate/index.d.ts.map +1 -1
  51. package/dist/commands/docs/index-generate/index.js +68 -12
  52. package/dist/commands/docs/init/index.d.ts +11 -0
  53. package/dist/commands/docs/init/index.d.ts.map +1 -1
  54. package/dist/commands/docs/init/index.js +19 -1
  55. package/dist/commands/gate/index.d.ts.map +1 -1
  56. package/dist/commands/gate/index.js +10 -2
  57. package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
  58. package/dist/commands/instructions/instructions.utils.js +42 -9
  59. package/dist/commands/internal/validate-skill-version-bumps.d.ts.map +1 -1
  60. package/dist/commands/internal/validate-skill-version-bumps.js +5 -5
  61. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  62. package/dist/commands/project/dispatch-ceiling/index.js +32 -18
  63. package/dist/commands/project/log/append.d.ts +48 -0
  64. package/dist/commands/project/log/append.d.ts.map +1 -1
  65. package/dist/commands/project/log/append.js +207 -30
  66. package/dist/commands/project/log/check.d.ts +87 -1
  67. package/dist/commands/project/log/check.d.ts.map +1 -1
  68. package/dist/commands/project/log/check.js +139 -5
  69. package/dist/commands/project/log/grammar.d.ts +165 -0
  70. package/dist/commands/project/log/grammar.d.ts.map +1 -1
  71. package/dist/commands/project/log/grammar.js +219 -1
  72. package/dist/commands/project/log/rollup.d.ts +16 -1
  73. package/dist/commands/project/log/rollup.d.ts.map +1 -1
  74. package/dist/commands/project/log/rollup.js +22 -2
  75. package/dist/commands/project/log/synthesize.d.ts +16 -0
  76. package/dist/commands/project/log/synthesize.d.ts.map +1 -1
  77. package/dist/commands/project/log/synthesize.js +85 -9
  78. package/dist/commands/status/index.d.ts.map +1 -1
  79. package/dist/commands/status/index.js +24 -2
  80. package/dist/commands/sync/apply.d.ts.map +1 -1
  81. package/dist/commands/sync/apply.js +6 -4
  82. package/dist/config/dispatch-matrix.d.ts.map +1 -1
  83. package/dist/config/dispatch-matrix.js +13 -5
  84. package/dist/config/oat-config.d.ts +15 -0
  85. package/dist/config/oat-config.d.ts.map +1 -1
  86. package/dist/config/oat-config.js +61 -7
  87. package/dist/config/own-keys.d.ts +22 -0
  88. package/dist/config/own-keys.d.ts.map +1 -0
  89. package/dist/config/own-keys.js +32 -0
  90. package/dist/config/resolve.d.ts +11 -0
  91. package/dist/config/resolve.d.ts.map +1 -1
  92. package/dist/config/resolve.js +35 -11
  93. package/dist/config/sync-config.d.ts.map +1 -1
  94. package/dist/config/sync-config.js +16 -0
  95. package/dist/drift/detector.d.ts.map +1 -1
  96. package/dist/drift/detector.js +52 -1
  97. package/dist/drift/skill-view-diagnostic.d.ts.map +1 -1
  98. package/dist/drift/skill-view-diagnostic.js +2 -23
  99. package/dist/engine/compute-plan.d.ts.map +1 -1
  100. package/dist/engine/compute-plan.js +22 -72
  101. package/dist/engine/managed-copy-hash.d.ts +69 -0
  102. package/dist/engine/managed-copy-hash.d.ts.map +1 -0
  103. package/dist/engine/managed-copy-hash.js +175 -0
  104. package/dist/fs/assets.d.ts +11 -0
  105. package/dist/fs/assets.d.ts.map +1 -1
  106. package/dist/fs/assets.js +1 -1
  107. package/dist/manifest/hash.d.ts +110 -0
  108. package/dist/manifest/hash.d.ts.map +1 -1
  109. package/dist/manifest/hash.js +111 -13
  110. package/dist/providers/ceiling/registry.d.ts.map +1 -1
  111. package/dist/providers/ceiling/registry.js +6 -1
  112. package/dist/providers/identity/dispatch-report.d.ts.map +1 -1
  113. package/dist/providers/identity/dispatch-report.js +4 -1
  114. package/dist/release/public-package-contract.d.ts.map +1 -1
  115. package/dist/release/public-package-contract.js +21 -0
  116. package/dist/validation/skills.d.ts.map +1 -1
  117. package/dist/validation/skills.js +305 -40
  118. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "oatVersion": "0.2.66"
3
+ "oatVersion": "0.2.68"
4
4
  }
@@ -93,7 +93,7 @@ Common keys in `.oat/config.json`:
93
93
  - `git.defaultBranch` — base branch fallback for PR workflows
94
94
  - `documentation.root`, `documentation.tooling`, `documentation.config` — docs-surface ownership
95
95
  - `documentation.excludes` — a JSON array of globs, relative to the docs directory, that `oat docs generate-index` leaves out of the generated index. `oat config set` takes the list as one comma-separated value (`"a/**,b.md"`) and stores it as an array; repeated `--exclude` flags extend it, and an empty value clears the key
96
- - `documentation.instructionPointerExcludes` — a JSON array of repository-relative directories that `oat instructions sync` and `oat instructions validate` must not treat as pointer sites, additive to the documentation content root they already skip by default (see [Instruction Sync](../provider-sync/instruction-sync.md#documentation-trees)). `oat config set` takes the list as one comma-separated value (`"vendor/generated,apps/oat-docs/docs"`) and stores it as an array of repository-relative POSIX paths, trimmed and de-duplicated; an empty value clears the key, and `oat config unset documentation.instructionPointerExcludes` removes it. An entry that could never exclude anything — an absolute path, or one escaping the repository with `..` — is rejected with exit code `1` rather than stored. A structurally malformed value already on disk (a non-array, or an empty or non-string entry) is rejected with exit code `2` and a repair message, and `oat config set` can still replace it. Entries that are well formed but cannot exclude anything — among them absolute paths, paths escaping the repository, paths that match no directory (matching is case-sensitive), and the unexcludable `.oat/repo` carve-in root — never abort the command; they are reported as warnings and are not counted as protection. Warnings go to stderr in human mode and to the `exclusionWarnings` field under `--json`
96
+ - `documentation.instructionPointerExcludes` — a JSON array of repository-relative directories that `oat instructions sync` and `oat instructions validate` must not treat as pointer sites, additive to the documentation content root they already skip by default (see [Instruction Sync](../provider-sync/instruction-sync.md#documentation-trees)). `oat config set` takes the list as one comma-separated value (`"vendor/generated,apps/oat-docs/docs"`) and stores it as an array of repository-relative POSIX paths, trimmed and de-duplicated; an empty value clears the key, and `oat config unset documentation.instructionPointerExcludes` removes it. An entry that could never exclude anything — an absolute path, or one escaping the repository with `..` — is rejected with exit code `1` rather than stored. A structurally malformed value already on disk (a non-array, or an empty or non-string entry) is rejected with exit code `2` and a repair message, and `oat config set` can still replace it. Entries that are well formed but cannot exclude anything — among them absolute paths, paths escaping the repository, paths that match no directory (matching is case-sensitive, and the warning names the on-disk spelling when the path differs only in case), paths that resolve elsewhere through a symlink (the warning names the resolved target instead of blaming case-sensitivity), and the unexcludable `.oat/repo` carve-in root — never abort the command; they are reported as warnings and are not counted as protection. Warnings go to stderr in human mode and to the `exclusionWarnings` field under `--json`
97
97
  - `documentation.requireForProjectCompletion` — whether docs sync is a completion gate
98
98
  - `archive.s3Uri` — base S3 archive prefix
99
99
  - `archive.s3SyncOnComplete` — upload archived projects to S3 during completion
@@ -274,6 +274,19 @@ packaged `assets/` directory next to the installed CLI. Setting a non-empty
274
274
  unset the override, never to rebuild or reinstall the CLI; a packaged-bundle
275
275
  failure keeps the rebuild/reinstall guidance. A directory that exists but
276
276
  cannot be read reports the underlying error code.
277
+ - The published npm tarball is held to the same seven-directory shape. The
278
+ CLI's public-package contract names a concrete file under each of the seven
279
+ directories, and release validation requires each one in the build workspace
280
+ and again in the packed tarball. The tarball check is the stronger of the two,
281
+ because a path can exist in the workspace and still never reach the tarball —
282
+ an empty directory that `npm pack` drops, or a `files`/symlink exclusion. So a
283
+ published package cannot ship a bundle whose top-level shape would fail
284
+ `validateBundleStructure`. That is a narrower promise than the exit-2 list
285
+ above: release validation does not check bundle metadata beyond its presence,
286
+ so a malformed or version-mismatched `bundle-metadata.json` is not covered.
287
+ The regression evidence is the negative pack control in
288
+ `packages/cli/src/release/public-package-contract.test.ts`:
289
+ `fails release validation when a required bundle directory is empty in the tarball`.
277
290
  - Produce a matching bundle with `bash packages/cli/scripts/bundle-assets.sh`
278
291
  while `OAT_ASSETS_DIR` points at the target directory (the script already
279
292
  honors the variable as its destination).
@@ -77,8 +77,10 @@ Two optional flags let an interrupted append be replayed safely:
77
77
 
78
78
  - `--idempotency-key <key>` — skip the append when an existing entry body
79
79
  already carries `key`, reporting `already-appended` instead of writing a
80
- duplicate. The key must appear in `--body`, since that is how a replay
81
- recognizes the entry it already wrote.
80
+ duplicate. The key must appear in `--body` as its own whitespace-delimited
81
+ word, since that is how a replay recognizes the entry it already wrote; a key
82
+ glued to varying text such as a timestamp never matches its earlier self.
83
+ Deduplication does not require `--commit`.
82
84
  - `--commit` — stage and commit the log after appending, retrying a bounded
83
85
  three attempts when the failure is a transient `.git/index.lock`.
84
86
 
@@ -101,6 +103,14 @@ instead.
101
103
 
102
104
  Run `oat project log append --help` for the complete entry contract.
103
105
 
106
+ Bodies may use line feeds or CRLF line endings. A CRLF body is accepted and
107
+ stored normalized to line feeds, and the result then carries
108
+ `normalizedLineEndings: true`, so the bytes on disk are known to differ from the
109
+ bytes passed. A lone carriage return, U+2028, or U+2029 anywhere in a body — including
110
+ at its very start or end — is refused, because those are the terminators the
111
+ log's readers do not all agree about. Structural entries take one line and
112
+ accept no line terminator at all.
113
+
104
114
  ## Automatic workflow integration
105
115
 
106
116
  When project logging is enabled, OAT lifecycle surfaces append structural
@@ -149,6 +159,25 @@ oat project log check --project .oat/projects/shared/example --json
149
159
  entry counts by class, type, and scope; the last entry date; and invalid
150
160
  hand-written headings. It reads only `project-log.md`.
151
161
 
162
+ It also reports whether the log carries a completion seal:
163
+
164
+ - `sealed`: `true` once the log holds a structural entry whose producer is
165
+ `oat-project-complete` and whose ref is `seal`. Both halves are required — a
166
+ `seal` ref from another producer is an ordinary entry.
167
+ - `seal`: `null` when unsealed, otherwise the first seal's `heading` and
168
+ `date`, whether it is `keyed`, and the `count` of seal entries. A `count`
169
+ above one is a log sealed twice before the seal append became idempotent.
170
+
171
+ `status` keeps its `ok` / `absent` / `synthesis_pending` values on a sealed log;
172
+ sealing is reported alongside the status, not as a status value. The one
173
+ additional status is `ambiguous`: a `##` heading, or a dated `###` entry
174
+ heading, starts a line after a lone carriage return, U+2028, or U+2029 rather
175
+ than a line feed, so the log's structure can be read two ways; or a seal is
176
+ physically present outside the parseable `## Entries` region. `check` then
177
+ exits 1 and reports an `ambiguity` reason instead of a clean verdict, every
178
+ writing command (`append`, `synthesize`, `rollup`) refuses the same file, and
179
+ the lifecycle skills stop rather than treat the log as empty.
180
+
152
181
  Use `--require-synthesis` to exit with status 1 while synthesis is pending:
153
182
 
154
183
  ```bash
@@ -156,7 +185,8 @@ oat project log check --require-synthesis
156
185
  ```
157
186
 
158
187
  Without that flag, normal `absent`, `ok`, and `synthesis_pending` results exit
159
- successfully so lifecycle skills can decide whether to warn or enforce.
188
+ successfully so lifecycle skills can decide whether to warn or enforce;
189
+ `ambiguous` always exits 1.
160
190
 
161
191
  ## Complete the synthesis
162
192
 
@@ -201,15 +231,44 @@ deduplicate by date and area. The command is idempotent.
201
231
 
202
232
  The structured result contains:
203
233
 
204
- - `status`: `ok` or `failed`
234
+ - `status`: `ok`, `failed`, or `ambiguous`
205
235
  - `summarySection`: `written` or `updated`
206
236
  - `ledgerOutcome`: `appended`, `deduplicated`, `skipped_permitted`, or `failed`
207
237
  - `entriesRolledUp`: number of log entries written to the summary section
208
238
 
239
+ An `ambiguous` result carries only `status` and an `ambiguity` reason, and exits
240
+ 1: the log's structure has two readings, so no count would mean anything and
241
+ neither `summary.md` nor the ledger is written. The other fields are absent
242
+ rather than zeroed, because `entriesRolledUp: 0` for an unreadable log is
243
+ indistinguishable from the same result for an empty one.
244
+
209
245
  `skipped_permitted` means the default repository reference layer is absent and
210
246
  no ledger path was explicitly configured; `status` remains `ok`. An explicitly
211
247
  configured ledger write failure returns `status: "failed"`. Completion must not
212
248
  seal or archive a project with entries until roll-up reports `status: "ok"`.
213
249
 
250
+ ## The completion seal
251
+
252
+ The seal is the last entry a project log may ever receive, and `append`
253
+ enforces that rather than leaving it to convention:
254
+
255
+ - Replaying the seal reports `already-appended` and leaves exactly one seal
256
+ entry. This holds for a seal carrying the completion skill's
257
+ `oat-seal:<project>` key and for an unkeyed seal written before that
258
+ convention, which is recognized by its heading instead.
259
+ - Every append carrying **new** content is refused with `status: "sealed"` and a
260
+ non-zero exit, naming the seal that closed the log.
261
+ - A replay that `--idempotency-key` recognizes as its own earlier entry is the
262
+ one exception: it reports `already-appended` and exits 0 even on a sealed log.
263
+ Nothing is appended — the entry it finds necessarily predates the seal — so
264
+ the seal stays the final entry. This is what keeps a gate
265
+ partial-finalization receipt replayable after the project is completed. The
266
+ key matches a whole word in an existing entry body, so an unrelated append
267
+ whose key happens to occur in some earlier entry is reported the same way
268
+ and likewise writes nothing.
269
+
270
+ A resumed completion therefore reads `sealed` from `check` and skips the
271
+ roll-up and the seal instead of duplicating them.
272
+
214
273
  `rollup` requires an existing `summary.md`; summary authoring remains the
215
274
  responsibility of the project summary workflow.
@@ -56,8 +56,8 @@ pnpm build:docs # builds docs site and its dependencies
56
56
  ```
57
57
 
58
58
  `pnpm test` is composite: it runs the workspace vitest suites, then
59
- `test:smoke` (`tools/smoke`), `test:skills` (`.agents/skills/*/tests`), and
60
- the named-file `test:release` set. Steps 5 and 6 are the version-lockstep
59
+ `test:smoke` (`tools/smoke`), `test:skills` (`.agents/skills/*/tests`), the
60
+ named-file `test:release` set, and last `test:scripts` (`scripts/worktree`). Steps 5 and 6 are the version-lockstep
61
61
  gates; they are in the local list because they previously ran only in CI and
62
62
  version-bump drift twice reached review with no local gate to surface it.
63
63
 
@@ -73,8 +73,12 @@ checkout with neither `origin/main` nor `main` skips the whole gate (CI uses
73
73
  `fetch-depth: 0`). Test files under `packages/cli/src/` count as publishable
74
74
  changes for this gate.
75
75
 
76
- CI runs neither `pnpm lint` nor `pnpm format` — run both whenever a change
77
- touches `tools/smoke` or `.agents/skills`, since nothing else covers them.
76
+ CI runs neither `pnpm lint` nor `pnpm format`. `pnpm check` now covers the
77
+ formatting of `.agents/skills/**`, `apps/oat-docs/docs`, and `tools/smoke`
78
+ through `format:root`, but `pnpm lint`'s root `oxlint` pass over `tools/smoke`
79
+ and `.agents/skills`, and `packages/control-plane`'s `format` (it defines no
80
+ `check` script), still run in no CI gate — run both whenever a change touches
81
+ those paths.
78
82
  For narrower changes, use package-specific checks when possible, but do not
79
83
  merge without passing the relevant workspace gates.
80
84
 
@@ -203,15 +203,19 @@ resolution. `resolveSkillVersion` in
203
203
  `packages/cli/src/commands/shared/frontmatter.ts` owns that order, so the
204
204
  runtime helper and the validators cannot disagree.
205
205
 
206
- The top-level `version` field remains supported as a deprecated alias. Every
207
- bundled skill now declares `metadata.version` and carries no top-level
208
- `version`, so `pnpm oat:validate-skills` reports no alias warnings on the
209
- bundled tree. The alias is retained for third-party skills installed from
210
- packs, which may still carry it; it retires on the schedule recorded in
211
- `DR-260908-bundled-skills-declare` — the warning becomes an error in the first
212
- release after 0.2.65 that changes the validator, and the resolver's top-level
213
- read is removed one release after that error has produced no findings on the
214
- bundled tree.
206
+ The top-level `version` field remains supported as a deprecated alias, and a
207
+ skill that declares it is now reported as an **error**. Every bundled skill
208
+ declares `metadata.version` and carries no top-level `version`, so
209
+ `pnpm oat:validate-skills` reports no alias findings on the bundled tree. The
210
+ alias is still read for third-party skills installed from packs, which may
211
+ carry it; it retires on the schedule recorded in
212
+ `DR-260908-bundled-skills-declare`. Step 1 of that schedule — promoting the
213
+ alias finding from a warning to an error — ships in the first release after
214
+ 0.2.65 that changes the validator. Step 2 removes the resolver's top-level read
215
+ one release after that error has produced no findings on the bundled tree; it
216
+ is tracked by `BL-260908-remove-the-top-level-skill` and must account for the
217
+ canonical agent roles, which the bump gate now enforces while they still
218
+ declare the top-level field.
215
219
 
216
220
  A skill that carries both fields with different values is a conflict. The
217
221
  resolver reports the `metadata.version` value together with the conflict, and
@@ -220,7 +224,7 @@ than silently picking one, and canonical role resolution treats the identity as
220
224
  invalid. The runtime version readers still return the `metadata.version` value,
221
225
  so an installed-versus-bundled comparison keeps working while the conflict is
222
226
  being fixed. Carrying both with the _same_ value resolves without a conflict and
223
- without an alias warning, because `metadata.version` wins. A bundled skill must
227
+ without an alias finding, because `metadata.version` wins. A bundled skill must
224
228
  still carry `metadata.version` alone: the corpus sweep in
225
229
  `packages/cli/src/validation/skills.test.ts` fails on any column-0 `version:`
226
230
  line under `.agents/skills`, which is the only check that catches the
@@ -228,16 +232,24 @@ same-value case.
228
232
 
229
233
  ### The version is gated
230
234
 
231
- Changing any canonical skill's `SKILL.md` requires bumping its frontmatter
232
- version in the same PR — one bump per changed skill in the final PR diff,
233
- even if the skill was edited multiple times on the branch. The gate reads the
234
- resolved version, so it enforces the bump whether the skill declares
235
+ Changing any bundled file of a canonical skill — anything under
236
+ `.agents/skills/<name>/` except `tests/`, because `tests/` is the one subtree
237
+ the bundle strips — requires bumping that skill's `metadata.version` in the
238
+ same PR, one bump per changed skill in the final PR diff, even if the skill was
239
+ edited multiple times on the branch. A change to a skill's `scripts/` or
240
+ `references/` therefore needs the bump just as an edit to `SKILL.md` does: both
241
+ ship to every `oat tools install` consumer. Changing a canonical agent role
242
+ under `.agents/agents/*.md` requires bumping that file's top-level `version:`;
243
+ agent roles deliberately keep the top-level field, so the gate accepts either
244
+ declaration shape and never demands `metadata.version` of them. The gate reads
245
+ the resolved version, so it enforces the bump whether the file declares
235
246
  `metadata.version` or the top-level alias. The rule is enforced by
236
247
  `pnpm run check:skill-bumps`, which runs locally (root `AGENTS.md` Definition
237
248
  of Done) and in CI; a changed skill whose version matches `origin/main` fails
238
- the gate. The deprecation warning above is deliberately not part of that gate:
239
- `check:skill-bumps` fails on any finding it receives, so an alias warning
240
- routed through it would fail every changed skill that still carried one.
249
+ the gate. The alias finding above is deliberately not part of that gate:
250
+ `check:skill-bumps` fails on any finding it receives regardless of severity, so
251
+ an alias finding routed through it would fail every changed skill that still
252
+ carried one.
241
253
 
242
254
  ## Practical Authoring Flow
243
255
 
@@ -132,11 +132,13 @@ Key behavior:
132
132
  - treats explicitly supplied `--docs-dir` / `--output` paths as overrides resolved from the current directory
133
133
  - fails with exit code `2` before generating or writing anything when an omitted path has no non-empty `documentation.root` to resolve against, or when the configured root is not a directory
134
134
  - refuses unsafe output targets with exit code `1` before generation, for derived and explicit paths alike, comparing symlink-resolved paths: inside the docs directory it indexes, equal to `documentation.config`, or ending in `.yml` / `.yaml` in any case. A derived output whose existing file lacks the `AUTOGENERATED` header is refused too; naming that path with `--output` overwrites it explicitly. A refused target is an actionable flag error (`1`); unusable path configuration is a separate condition and exits `2`, so scripts can branch on the two
135
+ - refuses a symlink chain that exceeds its own 32-hop cap, naming the path you supplied or the CLI derived rather than an intermediate link, and addressing the flag that owns it: an unusable **output** chain names `--output` and exits `1`; an unusable **docs directory** chain names `--docs-dir` and, when the directory was derived from `documentation.root` rather than passed explicitly, also names the `oat config set documentation.root <path>` repair and exits `2` as unusable path configuration. That cap governs the dangling links the command walks itself; a chain the operating system refuses first surfaces as its own `ELOOP` error with exit `1`, which is what a too-deep chain reports on a platform whose own limit is 32 or lower
135
136
  - writes configuration only for the Fumadocs manifest transition — recording the manifest it just wrote inside `documentation.root` in `documentation.index`, when `documentation.tooling` is `fumadocs` or the config declares neither `tooling` nor `config`. MkDocs configurations are never written, and `documentation.root` and `documentation.config` are never modified
136
137
  - prepends an `AUTOGENERATED` warning comment to the output and rewrites the file on every run; do not hand-edit the generated `index.md`
137
138
  - should be freshness-checked against authored `docs/**/index.md` `## Contents` maps before treating it as navigation evidence
138
139
  - sorting: `index.md` first, then directories before files, then lexical
139
- - reports the derived docs directory in human output and as `docsDir` / `docsDirSource` under `--json`
140
+ - reports the derived docs directory in human output and as `docsDir` / `docsDirSource` under `--json`, alongside `excludes` — the effective exclusion list, `documentation.excludes` merged with the `--exclude` flags exactly as it was handed to the generator
141
+ - says where to look when a manifest is empty rather than reporting a bare `0 entries`: which exclusion patterns were active, or that the docs directory held no indexable pages. The wording is observational — the command reports indexed entries, not pattern matches, so it names the exclusions without asserting they are the cause. An empty manifest is a report, not a failure — the file is still written, so a stale index is never left behind, and the command still exits `0`
140
142
  - omits pages matching the exclusion list. Nothing is excluded by default; once listed, non-page Markdown such as `CLAUDE.md` or `AGENTS.md` stops reaching the manifest. Patterns come from `documentation.excludes` in `.oat/config.json` and from repeated `--exclude` flags, and the flags **extend** the configured list rather than replacing it. A directory left empty by exclusion emits no heading, and an empty list produces byte-identical output to no exclusions at all
141
143
 
142
144
  Supported flags:
@@ -95,8 +95,8 @@ the manifest.
95
95
 
96
96
  - `missing-additive` — active, supported, no manifest entry, nothing at the expected path: the only true projection gap
97
97
  - `removed` — a manifest entry exists but the provider file is gone
98
- - `modified` — a manifest entry exists and the view no longer agrees with what the last sync recorded, or its version has fallen behind canonical. A `copy` view also reaches this class through the known limitation below, where the difference may be nothing but the generated banner
99
- - `in-sync` — a symlinked, collection-aliased, or natively read view _is_ the canonical file; a `copy` view matches the content recorded at its last sync
98
+ - `modified` — a manifest entry exists and the view no longer agrees with what the last sync recorded, or its version has fallen behind canonical
99
+ - `in-sync` — a symlinked, collection-aliased, or natively read view _is_ the canonical file; a `copy` view matches the content recorded at its last sync. A managed directory copy is compared with its OAT-managed banner and its `.oat-generated` sentinel excluded, so a freshly synced copy reads `in_sync` rather than drifted (owned by `skill-view-convergence.integration.test.ts`, "converges a copy-strategy skill directory")
100
100
  - `untracked` — something exists at the expected path that no manifest entry tracks; stray detection skips provider entries whose name matches a canonical entry, so `oat status` does not report it as a stray and reports the untracked projection as `missing` instead
101
101
  - `unverified` — the view's state is unknown: either a manifest entry arrived with no drift observation, or reading that one view failed and the row carries the redacted reason
102
102
  - `inactive`, `unsupported`, `excluded` — no projection is expected in this scope, so none of them is reported as missing
@@ -173,22 +173,6 @@ declared values and says the comparison was skipped, so the ambiguity is
173
173
  visible rather than silently resolved. When the resolver's answer happens to
174
174
  agree with canonical, nothing is withheld and the detail says so instead.
175
175
 
176
- ### Known limitation: copy-strategy skill views
177
-
178
- A `copy` skill view is reported `modified` immediately after a successful sync,
179
- and stays that way. The engine writes an OAT-managed banner into the copied
180
- `SKILL.md` and an `.oat-generated` sentinel beside it, and neither is accounted
181
- for in the manifest hash, so `oat status` reports `drifted:modified` and every
182
- later `oat sync --dry-run` plans another `update_copy`. Because no sync clears
183
- that state, `oat tools info` prints no repair line when a `modified` copy's
184
- version still matches canonical.
185
-
186
- That suppression is a conservative heuristic rather than a proof: equal versions
187
- do not establish equal bodies, so an edit to the canonical source or to the copy
188
- that kept the version reaches the same branch, and a sync _would_ repair that
189
- one. The class detail says so and names the concrete scope command to run if you
190
- know either side was edited. Tracked as `BL-260908-make-copy-strategy-skill`.
191
-
192
176
  ## Stray adoption
193
177
 
194
178
  `oat init` and `oat status` can offer adoption of unmanaged provider entries into canonical `.agents`.
@@ -109,8 +109,9 @@ child accepted by the native route that later returns `BLOCKED` are not
109
109
  role-selection rejection and do not permit fallback.
110
110
 
111
111
  Project workflows construct and redact the complete dispatch payload before
112
- the native call, then persist its accepted or `blocked-before-start` result
113
- immediately under the active project's `dispatch/` directory. The generic
112
+ the native call, then record its accepted or `blocked-before-start` result in
113
+ the project's run record; persisting it under the project's `dispatch/`
114
+ directory is optional and off by default. The generic
114
115
  record remains provider-neutral. OAT-specific canonical role, rejection,
115
116
  fallback, and runtime facts live only under its `oat` namespace. A qualifying
116
117
  fallback is a fresh request that preserves exact target and controls and is
@@ -153,7 +153,7 @@ Notable commands introduced in the current CLI surface:
153
153
  - `oat project complete-state <project-path>` - apply the canonical completed-state mutation to a project's `state.md`; used by `oat-project-complete` during lifecycle closeout
154
154
  - `oat project archive [project-path] [--project-recap-run <project-relative-path>] [--no-commit]` - archive a tracked project through the same local move, summary export, and configured S3 upload path used by completion. When omitted, the project path falls back to the active project. The optional recap path must identify a `project-recap` run inside the project's `explainers/` directory. Archive stages the selected complete package into `.oat/repo/reference/project-recaps/<YYYYMMDD-project-slug>/`, verifies every manifest-declared immutable file byte, then atomically installs the export before deleting the active project. Existing destinations, incomplete legacy hash coverage, stale bytes, path escapes, and recipe mismatches fail without removing the active project. For synced projects, a configured S3 upload must succeed before terminal cleanup. Successful closeout makes `refs/oat/completed/<project>` authoritative, deletes the tracked JSON record and nested checkout, and leaves archive metadata sufficient for an idempotent recordless retry. Legacy complete records receive a cleanup diagnosis rather than pull/open guidance. With `--json`, the terminal receipt reports `completedRef`, `verifiedSourceSha`, whether `activeAliasDisposition` was `retained` or `removed`, whether `recordRetired` completed, and the parent `lifecycleCommit`. `--no-commit` is a manual/library-only option: for a synced project it still removes the checkout but deliberately leaves `lifecycleCommit` null. Never pass it through `oat-project-complete`, which requires that receipt.
155
155
  - `oat repo archive sync [project-name]` - hydrate archived project snapshots from the configured repo-scoped S3 archive into `.oat/projects/archived/`. The old `oat project archive sync` path remains as a deprecated shim.
156
- - `oat project dispatch record --event-file <json-file-or-dash> [--project <project-path>]` - validate one generic dispatch record plus its namespaced OAT evidence event, and persist it into the project's append-only `dispatch/` journal. Omit `--project` to validate without persisting. Revision 1 is `dispatch/<request-id>.json` and every later revision is `dispatch/<request-id>@<NNNN>.json`; superseded revisions are never replaced or removed. The command validates and records evidence only — it never launches a provider. A `runtime-observation` event may carry either a finished `observation` or the provider `metadata` envelope (`provider`, `observedAt`, `entries`) to normalize, but not both. `entries` may be raw provider output: they are projected through the owning parser's allowlist, are never persisted, and both forms have their `match` derived from the record's configured invocation rather than trusted from the caller, with the observation's `provider` bound to the record's. With `--json`, the result adds `runtimeIdentity` with separate `configured` and `observed` objects plus a `match` of `matching`, `mismatching`, `not-comparable`, or `null`; observation is corroboration only and never overrides configured invocation. Error output is redacted on a best-effort basis; absolute paths in identity fields are rejected outright, while some prose forms (colon-prefixed, URL routes) can survive. See [Evidence Layers](../workflows/projects/evidence-layers.md).
156
+ - `oat project dispatch record --event-file <json-file-or-dash> [--project <project-path>]` - validate one generic dispatch record plus its namespaced OAT evidence event, and persist it into the project's append-only `dispatch/` journal (optional; no lifecycle skill requires it). Omit `--project` to validate without persisting. Revision 1 is `dispatch/<request-id>.json` and every later revision is `dispatch/<request-id>@<NNNN>.json`; superseded revisions are never replaced or removed. The command validates and records evidence only — it never launches a provider. A `runtime-observation` event may carry either a finished `observation` or the provider `metadata` envelope (`provider`, `observedAt`, `entries`) to normalize, but not both. `entries` may be raw provider output: they are projected through the owning parser's allowlist, are never persisted, and both forms have their `match` derived from the record's configured invocation rather than trusted from the caller, with the observation's `provider` bound to the record's. With `--json`, the result adds `runtimeIdentity` with separate `configured` and `observed` objects plus a `match` of `matching`, `mismatching`, `not-comparable`, or `null`; observation is corroboration only and never overrides configured invocation. Error output is redacted on a best-effort basis; absolute paths in identity fields are rejected outright, while some prose forms (colon-prefixed, URL routes) can survive. See [Evidence Layers](../workflows/projects/evidence-layers.md).
157
157
  - `oat project validate-plan --project-path <path>` - validates `oat_plan_parallel_groups` metadata in `plan.md`; exits non-zero on invalid. Lite plans must contain exactly one phase, declare no parallel groups, and give every Validation Criteria bullet either a backticked command or a `manual:` instruction. See [Implementation Execution](../workflows/projects/implementation-execution.md#parallel-phase-groups).
158
158
  - `oat project log append|check|synthesize|rollup` - manage the optional append-only project observation log: append validated judgment or structural entries, inspect grammar and synthesis status, complete end-of-run synthesis, and roll observations into `summary.md` plus the configured repository ledger. See [Project Log](../cli-utilities/project-log.md).
159
159
  - `oat project set-mode` — deprecated no-op. Execution mode is no longer user-selectable; emits a deprecation warning and preserves the `--json` contract.
@@ -75,9 +75,12 @@ This layer maps to Dispatch Report V1 and its provenance record; see the
75
75
  [Dispatch Report V1 / producer provenance](dispatch-ceiling.md#dispatch-report-v1-and-producer-provenance)
76
76
  section.
77
77
 
78
- For project-aware work, OAT persists this native dispatch lineage as one
79
- generic record per request under the project's `dispatch/` directory. The
80
- generic snake-case fields remain authoritative. A namespaced `oat` block adds
78
+ For project-aware work, the calling workflow records each launch in its run
79
+ record in `implementation.md` (request ID, `Dispatch:` stamp, launch status,
80
+ terminal outcome). Persisting one generic record per request under the
81
+ project's `dispatch/` directory with `oat project dispatch record` is optional
82
+ and off by default; no lifecycle skill or command consumes those files. When a
83
+ host opts in, the generic snake-case fields remain authoritative. A namespaced `oat` block adds
81
84
  only canonical-role identity, proven pre-start rejection, fallback linkage,
82
85
  and optional runtime observation. `DispatchReportV1` and the parseable
83
86
  `Dispatch:` compatibility stamp keep their existing byte shape.
@@ -73,9 +73,10 @@ The root does not implement phase tasks while an accepted phase launch owns
73
73
  that scope.
74
74
 
75
75
  Before a native launch, the root constructs and redacts the complete generic
76
- dispatch record and OAT role event. Immediately after the host returns, it runs
77
- `oat project dispatch record --project <project-path> --event-file - --json`
78
- with the accepted or `blocked-before-start` result. An accepted launch closes
76
+ dispatch record and OAT role event. When the host returns, it writes the
77
+ request ID, the `Dispatch:` stamp, and the accepted or `blocked-before-start`
78
+ result into the run record in `implementation.md`; persisting a per-dispatch
79
+ file with `oat project dispatch record` is optional and off by default. An accepted launch closes
79
80
  replacement. Only an explicit rejection proving no child started permits one
80
81
  fresh request that preserves the exact target and controls and is labeled as
81
82
  an approximation. Timeout, `BLOCKED`, refusal after acceptance, runtime
@@ -113,6 +113,15 @@ and retro is not added to the lite closeout sequence.
113
113
  On completion, OAT treats archive handling as an explicit closeout choice:
114
114
 
115
115
  - When archiving is selected, the local archive is written to `.oat/projects/archived/<project>/`.
116
+ - Every completion that will actually archive — any durable (`shared` or
117
+ `synced`) project with archive selected — keeps the `activeProject` pointer
118
+ until the archive returns a validated receipt, and only then clears it.
119
+ `local` projects are never durable and never archive, so they clear the
120
+ pointer immediately, as does any completion that declines archive. An archive
121
+ failure therefore leaves the pointer intact and the completion directly
122
+ resumable. See
123
+ [Picking Up a Project](picking-up-projects.md#archive-contents) for the
124
+ post-archive resume and its manual recovery path.
116
125
  - When archiving is disabled or declined, durable projects remain at their
117
126
  active path. Synced completion still finalizes and pushes the project ref,
118
127
  commits the discovery record as `complete`, retains the checkout and ref, and
@@ -183,6 +192,14 @@ each finding as a `deferred advisory` warning and continues, a project with no l
183
192
  final completion summary, and a resumed completion whose log already
184
193
  carries a seal runs report-only and appends nothing.
185
194
 
195
+ A resume detects that seal from `oat project log check`, which reports a
196
+ `sealed` field, rather than by re-reading the log. The invariant is enforced in
197
+ the CLI: replaying the seal reports `already-appended` and leaves exactly one
198
+ seal entry; a replay recognized by its own idempotency key reports
199
+ `already-appended` even on a sealed log (nothing is appended), and every
200
+ append carrying new content is refused with `status: "sealed"` and a non-zero
201
+ exit.
202
+
186
203
  Repositories that never adopted PJM skip the sweep with a one-line note. A
187
204
  missing planning surface degrades the sweep, never the closeout.
188
205
 
@@ -123,10 +123,12 @@ launch.
123
123
  ## Native Dispatch Lineage
124
124
 
125
125
  The launcher constructs and redacts the complete generic record plus the
126
- namespaced OAT role event before it calls the native host. Immediately after
127
- the host returns, the project adapter validates and persists the accepted or
128
- `blocked-before-start` state with `oat project dispatch record`. The command is
129
- a recorder only; it never launches a provider or changes selection authority.
126
+ namespaced OAT role event before it calls the native host. When the host
127
+ returns, the calling workflow writes the accepted or `blocked-before-start`
128
+ state into its run record in `implementation.md`. Persisting a per-dispatch
129
+ file with `oat project dispatch record` is optional and off by default; the
130
+ command is a recorder only and never launches a provider or changes selection
131
+ authority.
130
132
 
131
133
  An accepted handle owns the scope and closes replacement. One fresh fallback
132
134
  record is legal only when the native wrapper proves no child started, links the
@@ -99,6 +99,24 @@ If closeout is interrupted after terminal archive identity exists, rerun
99
99
  completion so it can resume from the completed ref and archive metadata. Do not
100
100
  use `pull` or `open` to recreate an active record or checkout.
101
101
 
102
+ For a `shared` project, archive moves the project into
103
+ `.oat/projects/archived/<project>/` and removes the source directory, while
104
+ completion keeps the `activeProject` pointer until the receipt validates.
105
+ Rerunning completion after an interruption between the archive and that clear
106
+ recognizes exactly this state — the pointer names a source directory that no
107
+ longer exists and the archive carries `oat_lifecycle: complete` plus the
108
+ `Lifecycle complete; archived locally` phase marker — so it validates the
109
+ discovered archive and clears the pointer without archiving a second time or
110
+ sealing the project log again. An interruption before the archive leaves the
111
+ source directory in place, so completion simply reruns from the start and skips
112
+ the work it already finished.
113
+
114
+ If the archive cannot be discovered — the source directory is gone and either
115
+ no archived project or several match the name — completion stops and leaves the
116
+ pointer untouched. Recover manually: locate the archive directory under
117
+ `.oat/projects/archived/`, confirm `oat_lifecycle: complete` in its `state.md`,
118
+ then clear the pointer with `oat config set activeProject ""`.
119
+
102
120
  ## Related
103
121
 
104
122
  - [Reviewing OAT PRs](reviewing-oat-prs.md)
@@ -72,9 +72,13 @@ Failure to publish a backlog item or issue does not invalidate a successfully wr
72
72
 
73
73
  Full repository audits use bounded read-only reconnaissance while the root
74
74
  agent retains classification, vetting, prioritization, cross-lane synthesis,
75
- and plan writing. Before launch, the caller loads the durable task classes and
76
- model-selection principles from `subagent-orchestration` plus exactly one
77
- active-provider selection reference.
75
+ and plan writing. Plan writes never move below the caller's model class — a
76
+ parallelized author runs on the caller's model and every plan is
77
+ caller-reviewed before publication or wave composition; the
78
+ `keeps external-plan writes on the caller's model class` case in
79
+ `skills-bundled-docs-contract.test.ts` is the backstop. Before launch, the
80
+ caller loads the durable task classes and model-selection principles from
81
+ `subagent-orchestration` plus exactly one active-provider selection reference.
78
82
 
79
83
  The internal `oat-dispatch-subagents` skill then owns capability checks, live
80
84
  catalog intersection, exact route selection, launch acceptance, recovery, and
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli": "0.2.66",
3
- "docs-config": "0.2.66",
4
- "docs-theme": "0.2.66",
5
- "docs-transforms": "0.2.66"
2
+ "cli": "0.2.68",
3
+ "docs-config": "0.2.68",
4
+ "docs-theme": "0.2.68",
5
+ "docs-transforms": "0.2.68"
6
6
  }