@open-agent-toolkit/cli 0.2.62 → 0.2.63

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 (62) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/config-and-local-state.md +1 -0
  3. package/assets/docs/cli-utilities/configuration.md +10 -8
  4. package/assets/docs/cli-utilities/project-log.md +16 -0
  5. package/assets/docs/cli-utilities/workflow-gates.md +124 -16
  6. package/assets/docs/provider-sync/commands.md +3 -0
  7. package/assets/docs/provider-sync/instruction-sync.md +107 -0
  8. package/assets/docs/reference/cli-reference.md +16 -6
  9. package/assets/docs/reference/oat-directory-structure.md +32 -31
  10. package/assets/docs/workflows/projects/lifecycle.md +45 -0
  11. package/assets/docs/workflows/projects/picking-up-projects.md +14 -0
  12. package/assets/docs/workflows/skills/explainer-kit.md +10 -4
  13. package/assets/docs/workflows/skills/repo-improve.md +15 -1
  14. package/assets/public-package-versions.json +4 -4
  15. package/assets/skills/oat-explainer-kit/SKILL.md +14 -1
  16. package/assets/skills/oat-explainer-kit/references/config-contract.md +12 -0
  17. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +57 -10
  18. package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +28 -5
  19. package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +371 -0
  20. package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +123 -1
  21. package/assets/skills/oat-project-autonomous/SKILL.md +10 -1
  22. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +121 -121
  23. package/assets/skills/oat-project-complete/SKILL.md +134 -4
  24. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +121 -121
  25. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  26. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +38 -6
  27. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +121 -121
  28. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +121 -121
  29. package/assets/skills/oat-project-next/SKILL.md +62 -19
  30. package/assets/skills/oat-project-plan/SKILL.md +31 -5
  31. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +121 -121
  32. package/assets/skills/oat-project-progress/SKILL.md +12 -3
  33. package/assets/skills/oat-project-quick-start/SKILL.md +264 -4
  34. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +121 -121
  35. package/assets/skills/oat-project-summary/SKILL.md +34 -23
  36. package/assets/skills/oat-repo-improve/SKILL.md +40 -5
  37. package/assets/skills/oat-repo-improve/references/plan-template.md +84 -7
  38. package/dist/commands/config/index.d.ts +1 -0
  39. package/dist/commands/config/index.d.ts.map +1 -1
  40. package/dist/commands/config/index.js +401 -18
  41. package/dist/commands/gate/index.d.ts +3 -1
  42. package/dist/commands/gate/index.d.ts.map +1 -1
  43. package/dist/commands/gate/index.js +699 -211
  44. package/dist/commands/init/tools/shared/skill-script-references.d.ts +126 -0
  45. package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -0
  46. package/dist/commands/init/tools/shared/skill-script-references.js +311 -0
  47. package/dist/commands/instructions/instructions.types.d.ts +63 -0
  48. package/dist/commands/instructions/instructions.types.d.ts.map +1 -1
  49. package/dist/commands/instructions/instructions.utils.d.ts +51 -2
  50. package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
  51. package/dist/commands/instructions/instructions.utils.js +201 -4
  52. package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
  53. package/dist/commands/instructions/sync/sync.js +12 -1
  54. package/dist/commands/instructions/validate/validate.d.ts.map +1 -1
  55. package/dist/commands/instructions/validate/validate.js +12 -1
  56. package/dist/commands/project/log/append.d.ts +195 -0
  57. package/dist/commands/project/log/append.d.ts.map +1 -1
  58. package/dist/commands/project/log/append.js +1034 -11
  59. package/dist/config/oat-config.d.ts +41 -0
  60. package/dist/config/oat-config.d.ts.map +1 -1
  61. package/dist/config/oat-config.js +111 -0
  62. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "oatVersion": "0.2.62"
3
+ "oatVersion": "0.2.63"
4
4
  }
@@ -120,6 +120,7 @@ Use `oat config` for repo runtime config inspection and supported key mutation.
120
120
 
121
121
  - `oat config get <key>` - read one resolved config value
122
122
  - `oat config set <key> <value>` - update a supported shared or repo-local key
123
+ - `oat config unset <key>` - remove a supported key from one surface, with the same `--shared`/`--local`/`--user` flags and per-key restrictions as `set`. Pruning removes any parent object the removal empties, and the resolved value falls back to the next surface or the built-in default. A key the surface does not hold reports an already-unset outcome and exits 0; `--json` adds a `removed` boolean that distinguishes that no-op from a real removal. Five classes are refused with exit 1: unknown keys (including the uncatalogued `documentation.index`), lifecycle state (`activeProject`, `lastPausedProject`, cleared with `oat config set <key> ''`), pack intent (`tools.*`, removed with `oat tools remove --pack <pack> --scope project`), the aggregate read views `workflow.dispatchCeiling` and `workflow.dispatchCeiling.providers`, and environment-shadowed keys with nothing stored on the target surface. See [CLI Reference](../reference/cli-reference.md#oat-config-surface-flags) for the full contract
123
124
  - `oat config list` - show the resolved command-surface values with source information
124
125
  - `oat config dump --json` - emit the full merged config payload with per-key source attribution, suitable for automation and debugging
125
126
  - `oat config describe` - list supported config surfaces and keys across shared repo, repo-local, user, and sync/provider config
@@ -18,13 +18,13 @@ For the deep file-by-file reference, see:
18
18
 
19
19
  ## The five config surfaces
20
20
 
21
- | Surface | File | Typical contents | Primary CLI surface |
22
- | ------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
23
- | Shared repo config | `.oat/config.json` | Repo-wide non-sync settings such as `projects.root`, `git.defaultBranch`, `documentation.*`, `archive.*`, `tools.*`, and shared `workflow.*` defaults | `oat config get/set/list/describe`, `oat gate` |
24
- | Repo-local config | `.oat/config.local.json` | Per-developer state for this checkout, such as `activeProject`, `lastPausedProject`, repo-local `activeIdea`, and local `workflow.*` overrides | `oat config get/set/list/describe`, `oat gate` |
25
- | User config | `~/.oat/config.json` | User-level state such as global `activeIdea` fallback and personal `workflow.*` defaults | `oat config describe`, `oat gate` |
26
- | Project sync config | `.oat/sync/config.json` | Provider enablement, sync strategy, and repo-level known stray settings | `oat providers set`, `oat config describe` |
27
- | User provider sync config | `~/.oat/sync/config.json` | User sync strategy and personal known provider strays | Provider-sync commands, `oat config describe` |
21
+ | Surface | File | Typical contents | Primary CLI surface |
22
+ | ------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
23
+ | Shared repo config | `.oat/config.json` | Repo-wide non-sync settings such as `projects.root`, `git.defaultBranch`, `documentation.*`, `archive.*`, `tools.*`, and shared `workflow.*` defaults | `oat config get/set/unset/list/describe`, `oat gate` |
24
+ | Repo-local config | `.oat/config.local.json` | Per-developer state for this checkout, such as `activeProject`, `lastPausedProject`, repo-local `activeIdea`, and local `workflow.*` overrides | `oat config get/set/unset/list/describe`, `oat gate` |
25
+ | User config | `~/.oat/config.json` | User-level state such as global `activeIdea` fallback and personal `workflow.*` defaults | `oat config get/set/unset/describe`, `oat gate` |
26
+ | Project sync config | `.oat/sync/config.json` | Provider enablement, sync strategy, and repo-level known stray settings | `oat providers set`, `oat config describe` |
27
+ | User provider sync config | `~/.oat/sync/config.json` | User sync strategy and personal known provider strays | Provider-sync commands, `oat config describe` |
28
28
 
29
29
  The main split is:
30
30
 
@@ -51,6 +51,7 @@ What each command is for:
51
51
  - `oat config list` shows the currently resolved command-surface values for shared and repo-local keys.
52
52
  - `oat config get <key>` reads one supported key value.
53
53
  - `oat config set <key> <value>` updates supported shared or repo-local keys.
54
+ - `oat config unset <key>` removes a supported key from one surface, using the same `--shared`/`--local`/`--user` flags and per-key restrictions as `set`. The resolved value then falls back to the next surface down, or to the built-in default. A key the surface does not hold exits 0 as already-unset (`--json` adds a `removed` boolean to tell the two apart). Unknown keys, lifecycle state, `tools.*` pack intent, the `workflow.dispatchCeiling` aggregate read views, and environment-shadowed keys with nothing stored are refused with exit 1 — see [CLI Reference](../reference/cli-reference.md#oat-config-surface-flags).
54
55
  - `oat config describe` shows the supported config catalog across shared repo, repo-local, user, and sync/provider surfaces.
55
56
  - `oat config describe <key>` shows file, scope, default, mutability, owning command, and description for one key.
56
57
 
@@ -91,6 +92,7 @@ Common keys in `.oat/config.json`:
91
92
  - `git.defaultBranch` — base branch fallback for PR workflows
92
93
  - `documentation.root`, `documentation.tooling`, `documentation.config` — docs-surface ownership
93
94
  - `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
95
+ - `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`
94
96
  - `documentation.requireForProjectCompletion` — whether docs sync is a completion gate
95
97
  - `archive.s3Uri` — base S3 archive prefix
96
98
  - `archive.s3SyncOnComplete` — upload archived projects to S3 during completion
@@ -768,7 +770,7 @@ Workflow preferences resolve through three config surfaces, with `local > shared
768
770
 
769
771
  ### Setting preferences
770
772
 
771
- `oat config set` supports mutually exclusive surface flags for workflow keys:
773
+ `oat config set` and `oat config unset` support the same mutually exclusive surface flags for workflow keys:
772
774
 
773
775
  ```bash
774
776
  # User-level: applies to all repos on this machine
@@ -71,6 +71,22 @@ oat project log append \
71
71
  Structural entries require `--structural`, `--producer`, `--ref`, and `--body`.
72
72
  Do not combine structural flags with judgment flags.
73
73
 
74
+ ### Idempotent append and commit
75
+
76
+ Two optional flags let an interrupted append be replayed safely:
77
+
78
+ - `--idempotency-key <key>` — skip the append when an existing entry body
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.
82
+ - `--commit` — stage and commit the log after appending, retrying a bounded
83
+ three attempts when the failure is a transient `.git/index.lock`.
84
+
85
+ Together they are the recovery entry point for a gate whose project-log
86
+ finalization could not commit. See
87
+ [Workflow Gates](./workflow-gates.md#project-log-finalization) for the receipt
88
+ and the exact recovery command.
89
+
74
90
  The helper produces these heading grammars in UTC:
75
91
 
76
92
  ```text
@@ -375,14 +375,14 @@ once an artifact exists, its `generatedAt` (the artifact's seconds-precision
375
375
  `oat_generated_at`), so a caller can correlate the result to the exact artifact
376
376
  and disambiguate re-gate rounds:
377
377
 
378
- | `status` | Exit | Meaning |
379
- | ------------------------------ | ---- | ------------------------------------------------------------ |
380
- | `ok` | 0 | Review completed; gate passed at the threshold. |
381
- | `blocked` | 1 | Review completed; findings at/above the threshold. |
382
- | `review_failed` | ≠0 | The provider target exited non-zero; no verdict. |
383
- | `artifact_missing` | 1 | The child exited cleanly without producing an artifact. |
384
- | `artifact_validation_failed` | 1 | Artifact format or configured invocation fields are invalid. |
385
- | `targeting_correlation_failed` | 1 | Identity did not correlate; do not run review-receive. |
378
+ | `status` | Exit | Meaning |
379
+ | ------------------------------ | ---- | --------------------------------------------------------------- |
380
+ | `ok` | 0 | Review completed; gate passed at the threshold. |
381
+ | `blocked` | 1 | Review completed; findings at/above the threshold. |
382
+ | `review_failed` | ≠0 | No validated verdict: the target or post-selection work failed. |
383
+ | `artifact_missing` | 1 | The child exited cleanly without producing an artifact. |
384
+ | `artifact_validation_failed` | 1 | Artifact format or configured invocation fields are invalid. |
385
+ | `targeting_correlation_failed` | 1 | Identity did not correlate; do not run review-receive. |
386
386
 
387
387
  Only `ok` and `blocked` are positive, receive-eligible review outcomes: both
388
388
  follow successful identity corroboration and carry a non-null `handoff`.
@@ -400,6 +400,37 @@ bookkeeping finish inline or through a synchronously awaited child, then start
400
400
  a new gate run. Do not use review-fix retries or review-receive for the failed
401
401
  run.
402
402
 
403
+ Post-selection recovery covers work that fails _after_ the reviewer already
404
+ committed a run-correlated artifact: corroboration, the threshold, handoff
405
+ construction, or result writing. Recovery means re-validation, never re-review.
406
+ The gate re-runs the same eligibility pipeline the normal path uses (project
407
+ containment, `oat_generated_at` validity, verdict parsing, gate-invocation
408
+ corroboration, the `oat_review_invocation: gate` marker, and the threshold)
409
+ against the immutable content/signature snapshot selected at correlation time.
410
+ It re-validates that snapshot rather than re-parsing whatever the path now
411
+ holds, and it never dispatches a second reviewer. The artifact must still be
412
+ present and unchanged for re-validation to succeed: a deleted or rewritten
413
+ artifact fails re-validation and the run stays failed.
414
+ When that re-validation succeeds the gate returns the ordinary `ok` or
415
+ `blocked` envelope with an additive `postSelectionRecovery: true`. Like
416
+ `lateCompletion`, that is recovery telemetry rather than a new status: route
417
+ review-receive from `status`, `receiveEligible`, and `handoff` as usual.
418
+
419
+ When no artifact was selected, or the selected snapshot does not re-validate,
420
+ the run stays `review_failed` with `outcome:
421
+ unexpected_post_selection_failure`. That envelope names the failing sub-step in
422
+ `postSelection.step` (`target-dispatch`, `artifact-scan`,
423
+ `artifact-correlation`, `artifact-validation`, `verdict-parse`,
424
+ `invocation-corroboration`, or `verdict-disposition`) and a routable
425
+ `postSelection.code`: the thrown error's own `code` or constructor name, the
426
+ eligibility cause when a committed artifact failed re-validation, or
427
+ `recovery_revalidation_failed` when the re-validation attempt itself threw. That
428
+ last case also emits a `gate-recovery-failed` diagnostic, so a persistent
429
+ non-recovery stays distinguishable from the transient failure that triggered it.
430
+ Replacement bytes at the artifact path, a missing artifact, a non-gate
431
+ invocation marker, and an artifact targeting another project never recover. Route deterministically on those
432
+ fields instead of re-dispatching the reviewer.
433
+
403
434
  `ok` and `blocked` also include `receiveEligible: true`, `outcome`,
404
435
  `artifactPath`, `counts`, `scope`, `handoff`, `gateInvocation`, and
405
436
  `corroboration`. Invoke review-receive only when all three conditions hold:
@@ -445,6 +476,81 @@ oat --json gate review \
445
476
  Read the resulting envelope and exit code; that is the whole completion
446
477
  contract.
447
478
 
479
+ ### Project log finalization
480
+
481
+ After the envelope is written, the gate appends one structural entry to the
482
+ project's `project-log.md` and commits it, so the run does not leave a dirty
483
+ worktree for whatever executes next. The entry body ends with
484
+ `run=<runId>` — the same run id the envelope carries. That token is the entry's
485
+ stable identity: a retry or a later recovery recognizes the entry it already
486
+ wrote instead of appending a second one.
487
+
488
+ Staging and committing the log retries **three attempts** separated by two
489
+ waits (250 ms, then 500 ms) when, and only when, git itself reports index-lock
490
+ contention: `Unable to create '…/index.lock': File exists`, or git's `Another
491
+ git process seems to be running in this repository` advice in output that also
492
+ names an index lock. Every other failure — a hook,
493
+ signing, identity, or pathspec error — is reported immediately rather than
494
+ retried, including one whose output merely mentions `index.lock`. The gate never
495
+ deletes, moves, or forces a lock it did not take. A lock whose mtime is
496
+ unchanged across the whole retry window is reported as `persistent-index-lock`;
497
+ one that moves is `transient-index-lock`. If a competing writer commits the same
498
+ entry while the gate retries, the gate reports the work as already committed
499
+ instead of a failure — but only when the committed log still carries this run's
500
+ entry.
501
+
502
+ When the retries are exhausted the append has landed but the commit has not, so
503
+ the gate writes a durable receipt to
504
+ `<project>/gate-receipts/<runId>.json` and emits a
505
+ `gate-project-log-partial-finalization` diagnostic naming the receipt and
506
+ printing its recovery command verbatim. The gate result is unchanged: a passing
507
+ review still exits `ok`.
508
+
509
+ The receipt must not dirty the worktree, so this repository ignores
510
+ `**/gate-receipts/` repository-wide rather than under `.oat/projects` alone:
511
+ `projects.root` is settable, and the receipt follows the resolved project. A
512
+ repository whose ignore rules do not cover the receipt still gets the receipt —
513
+ losing the finalization would be worse than a tracked file — plus a
514
+ `gate-project-log-receipt-warning` diagnostic naming the path, so the gap
515
+ surfaces before the next repository-wide `git add`.
516
+
517
+ The receipt completes the finalization from a later process, with no reviewer
518
+ and no second gate run:
519
+
520
+ ```bash
521
+ oat project log append --project <projectPath> --structural \
522
+ --producer 'oat gate review' --ref <ref> --body <body> \
523
+ --idempotency-key <runId> --commit
524
+ ```
525
+
526
+ Before appending anything, that command validates the receipt against the live
527
+ tree: the project path must resolve to the same project, `worktreeRoot` must
528
+ match `git rev-parse --show-toplevel`, and the review artifact must still exist
529
+ with its recorded sha256 signature. A receipt that names no artifact — the gate
530
+ correlated none, so `artifactPath` and `artifactSignature` are both `null` — is
531
+ bound by run id, project, worktree, and the entry fields alone; a receipt that
532
+ names an artifact _without_ a signature is malformed and refused. The replayed
533
+ entry must also be the entry
534
+ the receipt describes — `--producer`, `--ref`, and `--body` are compared against
535
+ the receipt, and the receipt's own run id must be the `--idempotency-key` that
536
+ names its file — so a receipt can never be consumed while some other entry is
537
+ written. Any mismatch exits non-zero with a
538
+ `gate-project-log-receipt-mismatch` diagnostic and touches nothing. On a match
539
+ it appends (or reports `already-appended` when the entry is already there),
540
+ commits with the same bounded retry, and deletes the receipt. Running it twice
541
+ is safe: the second run appends nothing and creates no commit.
542
+
543
+ At the start of a run, a receipt still sitting under the project produces a
544
+ `gate-project-log-receipt-pending` diagnostic naming the receipt and its
545
+ recovery command. Its `state` is `stale` only when the _committed_ log
546
+ (`HEAD:project-log.md`) already carries that run id and entry — the append
547
+ landed and was committed, so recovery will observe `already-appended` and clear
548
+ the receipt. Everything else is `pending`, including the ordinary case where
549
+ the entry is in the working tree but no commit succeeded: that is precisely
550
+ what the receipt exists to finish, so it is never reported as leftovers. A
551
+ receipt naming another tree's log, or a committed log that cannot be read back,
552
+ also stays `pending`. The warning is discovery only; the gate still runs.
553
+
448
554
  ## Exec targets
449
555
 
450
556
  `oat gate cross-provider-exec` chooses from `workflow.gates.execTargets`.
@@ -884,14 +990,16 @@ those artifacts; correct the project/run correlation and start a new gate run.
884
990
 
885
991
  ### Incident-to-regression mapping
886
992
 
887
- | Observed failure | Regression coverage |
888
- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
889
- | Headless reviewer could not complete an async delegation | route unit matrix plus canonical checkout-local command, strict receipt, headless inline, and structured-refusal cases |
890
- | Large final review exceeded the old 15-minute budget | scope-aware resolver tests plus scaled final-scope fake-runtime case |
891
- | Silent child looked idle while transcripts grew | metadata-probe tests plus timeout-with-advancing-transcript fixture |
892
- | Timeout or child failure produced no artifact | fail-closed `noOutputProduced` fixture |
893
- | Artifact carried the wrong gate run ID | provenance-mismatch fixture |
894
- | Passing artifact lost receive routing | handoff and `receiveEligible` fixture |
993
+ | Observed failure | Regression coverage |
994
+ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
995
+ | Headless reviewer could not complete an async delegation | route unit matrix plus canonical checkout-local command, strict receipt, headless inline, and structured-refusal cases |
996
+ | Large final review exceeded the old 15-minute budget | scope-aware resolver tests plus scaled final-scope fake-runtime case |
997
+ | Silent child looked idle while transcripts grew | metadata-probe tests plus timeout-with-advancing-transcript fixture |
998
+ | Timeout or child failure produced no artifact | fail-closed `noOutputProduced` fixture |
999
+ | Artifact carried the wrong gate run ID | provenance-mismatch fixture |
1000
+ | Passing artifact lost receive routing | handoff and `receiveEligible` fixture |
1001
+ | Passing review reported as a failed gate after a post-selection error | post-selection recovery cases plus snapshot-replacement, non-gate-marker, and foreign-target controls |
1002
+ | Transient index lock turned a completed review into a failed gate | index-lock retry cases plus persistent/transient classification, receipt fixture, and fresh-process recovery control |
895
1003
 
896
1004
  ## Current limits
897
1005
 
@@ -210,6 +210,7 @@ Key behavior:
210
210
  - Supports `--strategy pointer|symlink|copy` to validate the expected file shape
211
211
  - Reports `ok`, `missing`, `content_mismatch`, and `stray` states
212
212
  - Detects Claude-only adoptable directories and unreadable/broken instruction paths as drift
213
+ - Skips the derived documentation content root and any `documentation.instructionPointerExcludes` paths, using the same exclusions as `oat instructions sync`
213
214
  - Exit code `0` when all entries are valid, `1` when drift is detected
214
215
  - Detailed behavior: [`Instruction Sync`](instruction-sync.md)
215
216
 
@@ -227,5 +228,7 @@ Key behavior:
227
228
  - Adopts Claude-only stray files by writing canonical `AGENTS.md` content first, then regenerating `CLAUDE.md`
228
229
  - Skips mismatched files unless `--force` is provided
229
230
  - Skips unreadable canonical or Claude-only sources and reports manual-repair guidance instead of forcing recovery
231
+ - Skips the documentation content tree (`<documentation.root>/docs` when that is a directory, otherwise `documentation.root`) plus any `documentation.instructionPointerExcludes` paths, so authored pages never receive pointers; app-level instruction files such as `apps/oat-docs/AGENTS.md` are still synced
232
+ - Never deletes an existing `CLAUDE.md` inside an excluded tree
230
233
  - Uses pointer content `@AGENTS.md\n`, file symlinks, or hard copies depending on the selected strategy
231
234
  - Detailed behavior and examples: [`Instruction Sync`](instruction-sync.md)
@@ -23,8 +23,115 @@ Instruction sync is currently project-only.
23
23
  - It supports nested directories all the way down the tree.
24
24
  - It skips provider-irrelevant or local-only roots such as `.git`, `.oat`, `.worktrees`, and `node_modules`.
25
25
  - Exception: `.oat/repo/**` is scanned even though the rest of `.oat/` is skipped, so the curated `AGENTS.md` files there (repo root guidance, `pjm/`, `reference/`) get their sibling `CLAUDE.md` shims managed and validated like any other directory. The rest of `.oat/` (`templates/`, `projects/`, `sync/`) stays excluded.
26
+ - It skips the documentation content tree by default, and any path you add to `documentation.instructionPointerExcludes`. See [Documentation trees](#documentation-trees) below.
26
27
  - It does not scan user-level provider roots such as `~/.claude` in this release.
27
28
 
29
+ ## Documentation Trees
30
+
31
+ A documentation tree holds authored pages, not instructions for agents. A
32
+ `CLAUDE.md` written into one becomes a published page, and a page legitimately
33
+ named `CLAUDE.md` is content that must not be treated as a pointer. Both
34
+ `oat instructions validate` and `oat instructions sync` therefore skip the
35
+ documentation content root, so validate never reports drift that sync refuses
36
+ to fix.
37
+
38
+ The content root is derived from `documentation.root` in `.oat/config.json`:
39
+
40
+ - `<documentation.root>/docs` when that path is a directory;
41
+ - otherwise `documentation.root` itself.
42
+
43
+ This is the same derivation `oat docs generate-index` uses to pick its default
44
+ docs directory, so the excluded tree and the indexed tree agree by
45
+ construction. (`oat docs generate-index --docs-dir` overrides the index side
46
+ only; it does not change what instruction sync excludes.)
47
+
48
+ **App-level instruction files are still synced.** `documentation.root`
49
+ canonically names the docs _app_ root, and a file like
50
+ `apps/oat-docs/AGENTS.md` is instructions for working on the docs app rather
51
+ than a documentation page. When the app root has a `docs` child, only that
52
+ child is skipped, and the app root keeps receiving its `CLAUDE.md` pointer. Opt
53
+ the app root out explicitly if you do not want it synced.
54
+
55
+ Add further paths with `documentation.instructionPointerExcludes`, a list of
56
+ repository-relative directories. Set it with
57
+ `oat config set documentation.instructionPointerExcludes "vendor,third_party/docs"`
58
+ (an empty value clears it, and `oat config unset` removes it), or write it
59
+ directly:
60
+
61
+ ```json
62
+ {
63
+ "documentation": {
64
+ "root": "apps/oat-docs",
65
+ "instructionPointerExcludes": ["vendor", "third_party/docs"]
66
+ }
67
+ }
68
+ ```
69
+
70
+ Each entry excludes that directory and everything beneath it, matching the
71
+ directory path exactly rather than by prefix — excluding `apps/docs` leaves a
72
+ sibling `apps/docs-legacy` scanned. Entries are repository-relative and
73
+ normalized, so `apps/./docs`, `apps//docs`, and `apps/docs/` all name the same
74
+ tree; absolute paths and paths escaping the repository are dropped with a
75
+ warning when they are already stored — and refused outright by
76
+ `oat config set` — and the repository root itself cannot be excluded. A structurally malformed value
77
+ (anything other than an array of non-empty strings) is rejected with exit code
78
+ `2` rather than silently ignored.
79
+
80
+ The list is additive to the derived content root, and it is applied after the
81
+ `.oat/repo` carve-in, so excluding _another_ tree can never collaterally leave
82
+ `.oat/repo/**` unscanned. `.oat/repo` itself is never excludable — listing it
83
+ is reported as having no effect rather than silently honoured, and its
84
+ `AGENTS.md` keeps its pointer. A deliberate opt-out naming a path _beneath_
85
+ it, such as `.oat/repo/pjm`, is honoured: descendants are reached by ordinary
86
+ traversal, so only the carve-in root is protected from exclusion.
87
+
88
+ ### When an exclusion does nothing
89
+
90
+ An entry that is well formed but cannot protect anything — it names a
91
+ directory that does not exist, differs in case from the real path on a
92
+ case-insensitive filesystem such as APFS, is absolute, escapes the repository,
93
+ or is the unexcludable `.oat/repo` — does not fail the command. It is reported
94
+ as a warning naming the entry, and it is never counted as protection.
95
+
96
+ The warning channel depends on the mode: in human mode warnings are written to
97
+ stderr, and under `--json` they are carried in the `exclusionWarnings` payload
98
+ field instead, because `--json` suppresses stderr warnings entirely.
99
+
100
+ That distinction matters most on a case-insensitive filesystem: a
101
+ `documentation.root` of `Apps/Docsapp` resolves happily while the scan compares
102
+ the real `apps/docsapp`, so the tree would be scanned after all. Trust
103
+ `effectiveExcludedPaths`, not `excludedPaths`, when deciding whether a tree is
104
+ protected — and read `exclusionWarnings` for the reason, since the stderr
105
+ warnings are silent under `--json`.
106
+
107
+ Because both commands read `.oat/config.json` to resolve exclusions, they
108
+ inherit its validation. Any value that fails a _fail-closed_ field check —
109
+ including keys these commands do not otherwise use, such as
110
+ `documentation.excludes` — makes `oat instructions sync` and
111
+ `oat instructions validate` exit `2` until it is repaired. Not every key is
112
+ fail-closed: a wrong-typed scalar such as `documentation.root` is dropped
113
+ rather than rejected, so it never aborts the command (a dropped `root` simply
114
+ leaves the content root underived, and no default exclusion applies).
115
+
116
+ Exclusion only stops a directory from being scanned. Nothing is deleted: a
117
+ `CLAUDE.md` that already exists inside an excluded tree is left exactly as it
118
+ is. When any exclusion is configured, `--json` output carries three additional
119
+ fields.
120
+
121
+ - `excludedPaths` lists the configured exclusions — a statement of intent, not
122
+ a per-directory skip log, so `.oat` appearing there does not contradict
123
+ `.oat/repo` still being scanned.
124
+ - `effectiveExcludedPaths` lists the subset that names a real, case-exact
125
+ directory the scan actually pruned. It is present whenever `excludedPaths`
126
+ is, including as an empty array when every configured entry turned out to be
127
+ inert.
128
+ - `exclusionWarnings` lists one message per configured exclusion that will not
129
+ protect anything — the `--json` counterpart of the stderr warnings above.
130
+
131
+ Each field is omitted when it would be empty. `exclusionWarnings` is
132
+ independent of the other two: an absolute or repository-escaping entry is
133
+ dropped before `excludedPaths` is built, so it appears only here.
134
+
28
135
  ## Canonical Model
29
136
 
30
137
  - `AGENTS.md` is canonical.
@@ -149,7 +149,7 @@ Notable commands introduced in the current CLI surface:
149
149
  - `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).
150
150
  - `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).
151
151
  - `oat project set-mode` — deprecated no-op. Execution mode is no longer user-selectable; emits a deprecation warning and preserves the `--json` contract.
152
- - `oat gate review <prompt...>` - run a stateful, headless OAT review through the target registry, parse the produced review artifact, and exit nonzero for configured blocking findings. `--timeout-ms <milliseconds>` overrides target, `workflow.gateTimeouts`, environment, and scope defaults; accepted values are 1,000–14,400,000. Final/phase/range code reviews default to 30 minutes, while task code and artifact reviews default to 15 minutes. With `--json`, the result envelope on exit is the canonical completion signal: `status` is `ok` | `blocked` | `review_failed` | `artifact_missing` | `artifact_validation_failed` | `targeting_correlation_failed`, alongside `runId`, `generatedAt`, and `artifactPath` when available. `artifact_missing` means an accepted child exited cleanly without an artifact; fix synchronous review/artifact completion and start a new run, without review-receive or same-run remediation. For `targeting_correlation_failed`, correct the project declaration or artifact routing and start a new run; do not run review-receive even if an artifact path is present. For `artifact_validation_failed`, correct the artifact and rerun the gate until it revalidates as `ok` or `blocked`. Invoke `oat-project-review-receive` only when all three conditions hold: `status` is `ok` or `blocked`, `receiveEligible` is `true`, and `handoff` is non-null. `review_failed` may include structured `refusal`, `noOutputProduced`, and metadata-only `activityEvidence`; these diagnostic fields never make a run receive-eligible. After a timeout, a validated run-correlated artifact returns the ordinary `ok` or `blocked` envelope with additive `lateCompletion: true`. Duplicate run-ID matches or a changed artifact with a mismatched run ID retain `targeting_correlation_failed` and `receiveEligible: false`. Orchestrators should read the structured result rather than poll the filesystem. See [Workflow Gates](../cli-utilities/workflow-gates.md).
152
+ - `oat gate review <prompt...>` - run a stateful, headless OAT review through the target registry, parse the produced review artifact, and exit nonzero for configured blocking findings. `--timeout-ms <milliseconds>` overrides target, `workflow.gateTimeouts`, environment, and scope defaults; accepted values are 1,000–14,400,000. Final/phase/range code reviews default to 30 minutes, while task code and artifact reviews default to 15 minutes. With `--json`, the result envelope on exit is the canonical completion signal: `status` is `ok` | `blocked` | `review_failed` | `artifact_missing` | `artifact_validation_failed` | `targeting_correlation_failed`, alongside `runId`, `generatedAt`, and `artifactPath` when available. `artifact_missing` means an accepted child exited cleanly without an artifact; fix synchronous review/artifact completion and start a new run, without review-receive or same-run remediation. For `targeting_correlation_failed`, correct the project declaration or artifact routing and start a new run; do not run review-receive even if an artifact path is present. For `artifact_validation_failed`, correct the artifact and rerun the gate until it revalidates as `ok` or `blocked`. Invoke `oat-project-review-receive` only when all three conditions hold: `status` is `ok` or `blocked`, `receiveEligible` is `true`, and `handoff` is non-null. `review_failed` may include structured `refusal`, `noOutputProduced`, and metadata-only `activityEvidence`; these diagnostic fields never make a run receive-eligible. After a timeout, a validated run-correlated artifact returns the ordinary `ok` or `blocked` envelope with additive `lateCompletion: true`. When post-selection work fails after the reviewer already committed a run-correlated artifact, the gate re-validates that selected snapshot through the same eligibility path rather than re-parsing whatever the path now holds, never re-dispatching, and returns the ordinary `ok` or `blocked` envelope with additive `postSelectionRecovery: true`; the artifact must still be present and unchanged for re-validation to succeed. When nothing recovers, `review_failed` names the failing sub-step in `postSelection.step` and its cause in `postSelection.code`. Duplicate run-ID matches or a changed artifact with a mismatched run ID retain `targeting_correlation_failed` and `receiveEligible: false`. Orchestrators should read the structured result rather than poll the filesystem. See [Workflow Gates](../cli-utilities/workflow-gates.md).
153
153
  - `oat gate route --expect-runtime <runtime> --expect-model <model> --can-await <true|false> --json` - return the headless child route (`inline`, `delegate-sync`, or `refuse`) from provider-marker and model evidence. Ambiguous or contradictory evidence never routes inline.
154
154
  - `oat gate exec <prompt...>` / `oat gate cross-provider-exec <prompt...>` - run a generic target prompt. `--timeout-ms` uses the same validated CLI override; untyped runs otherwise retain the legacy default path.
155
155
  - `oat gate target set <id> --invocation-model <model|provider-default> --invocation-reasoning-effort <effort|provider-default>` - persist optional configured invocation metadata alongside an exec target without inferring it from the target command.
@@ -159,15 +159,25 @@ Notable commands introduced in the current CLI surface:
159
159
 
160
160
  ## `oat config` surface flags
161
161
 
162
- `oat config set` supports mutually exclusive surface flags that control which config file receives the write:
162
+ `oat config set` and `oat config unset` support the same mutually exclusive surface flags, which control the config file the write or removal lands in:
163
163
 
164
- - `--shared` — write to `.oat/config.json` (committed team repo settings)
165
- - `--local` — write to `.oat/config.local.json` (per-developer repo state, gitignored)
166
- - `--user` — write to `~/.oat/config.json` (user-level fallback, applies across all repos)
164
+ - `--shared` — write to or remove from `.oat/config.json` (committed team repo settings)
165
+ - `--local` — write to or remove from `.oat/config.local.json` (per-developer repo state, gitignored)
166
+ - `--user` — write to or remove from `~/.oat/config.json` (user-level fallback, applies across all repos)
167
167
 
168
168
  When no flag is passed, the CLI picks a sensible default per key type: structural keys (`projects.root`, `documentation.*`, etc.) go to shared, state keys (`activeProject`, etc.) go to local, workflow preferences (`workflow.*`) go to local. Pass at most one flag — the command rejects multiple surface flags.
169
169
 
170
- Per-key restrictions apply: structural keys can only be written at shared scope, most state keys can only be written at local scope (`activeIdea` is the exception — it accepts both local and user), and workflow preference keys accept any non-auto surface. Legacy `autoReviewAtCheckpoints` remains shared-only; prefer `workflow.autoReviewAtHillCheckpoints`.
170
+ Per-key restrictions apply identically to `set` and `unset`: structural keys can only be written at shared scope, most state keys can only be written at local scope (`activeIdea` is the exception — it accepts both local and user), and workflow preference keys accept any non-auto surface. Legacy `autoReviewAtCheckpoints` remains shared-only; prefer `workflow.autoReviewAtHillCheckpoints`.
171
+
172
+ `oat config unset <key>` removes the key from the selected surface and prunes any parent object it empties, so the resolved value falls back through the remaining surfaces to the built-in default. Removing a key the surface does not hold is not an error: it reports an already-unset outcome and exits 0. With `--json` the envelope adds a `removed` boolean, which is the machine-readable way to tell a real removal from an already-unset no-op, since both exit 0. A stored value that fails validation is still removed, matching `oat config set`, which repairs the same file.
173
+
174
+ `unset` refuses five classes of key, each with exit code 1:
175
+
176
+ - **Unknown keys**, including keys OAT reads but does not expose in the `oat config` catalog (`documentation.index`). These are not removable by any `oat config` command.
177
+ - **Lifecycle state** (`activeProject`, `lastPausedProject`) — cleared with `oat config set <key> ''` instead.
178
+ - **Pack intent** (`tools.*`) — removed with `oat tools remove --pack <pack> --scope project`, which also removes the installed pack files. Clearing the intent alone would leave them behind.
179
+ - **Aggregate read views** (`workflow.dispatchCeiling`, `workflow.dispatchCeiling.providers`) — these are assembled for `get` from the leaf keys and are not stored as such. Unset `workflow.dispatchCeiling.preset` or `workflow.dispatchCeiling.providers.<provider>` instead.
180
+ - **Environment-shadowed keys with nothing stored on the target surface** — when `OAT_PROJECTS_ROOT`, `OAT_PROJECTS_DEFAULT_SCOPE`, or `OAT_WORKTREES_ROOT` supplies the effective value and the surface holds nothing to remove, clear the environment variable in your shell. When the surface _does_ hold a stored value, `unset` removes it (as `set` would rewrite it) and warns that the override still supplies the effective value.
171
181
 
172
182
  ## `workflow.*` preference keys
173
183
 
@@ -110,37 +110,38 @@ Legacy `.oat/active-project` / `.oat/projects-root` / `.oat/active-idea` files m
110
110
 
111
111
  Current schema keys:
112
112
 
113
- | Key | Type | Default | Description |
114
- | ---------------------------------------------------------- | -------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
115
- | `version` | `number` | `1` | Schema version |
116
- | `worktrees.root` | `string` | `".worktrees"` | Root directory for git worktrees (repo-relative or absolute) |
117
- | `projects.root` | `string` | `".oat/projects/shared"` | Default root directory for OAT projects |
118
- | `projects.defaultScope` | `string` | `"synced"` | Scope used by project creation when `--scope` is omitted: `shared`, `local`, or `synced`. Environment override: `OAT_PROJECTS_DEFAULT_SCOPE`. |
119
- | `localPaths` | `string[]` | - | Gitignored directories to sync between main repo and worktrees. Supports glob patterns. Managed via `oat local add/remove`. |
120
- | `documentation.root` | `string` | - | Docs **app root** — the directory `oat docs init` scaffolds (e.g., `apps/docs`). Authored pages live under `<root>/docs`. See [Documentation path resolution](#documentation-path-resolution). |
121
- | `documentation.tooling` | `string` | - | Documentation framework identifier (`mkdocs` or `fumadocs`) |
122
- | `documentation.config` | `string` | - | Path to the documentation framework config file (e.g., `mkdocs.yml`, `next.config.js`) |
123
- | `documentation.index` | `string` | - | Path to the docs surface entry point: the generated app-root manifest `<root>/index.md` for Fumadocs, `mkdocs.yml` for MkDocs. Set by `oat docs init`. `oat docs generate-index` writes it only for the Fumadocs manifest transition and never for MkDocs. |
124
- | `documentation.excludes` | `string[]` | - | Globs, relative to the docs directory, that `oat docs generate-index` leaves out of the generated index. See [Excluding pages from the generated index](#excluding-pages-from-the-generated-index). |
125
- | `documentation.requireForProjectCompletion` | `boolean` | `false` | When `true`, OAT project completion gates require documentation to be updated |
126
- | `git.defaultBranch` | `string` | `"main"` | Default branch for PR creation. Auto-detected during `oat init` via `gh repo view` or `origin/HEAD`. Used by `oat-project-pr-final` and `oat-project-pr-progress`. |
127
- | `workflow.autoReviewAtHillCheckpoints` | `boolean` | unset | When `true`, completing a HiLL checkpoint automatically runs the extra lifecycle review. Does not control Tier 1 per-phase `oat-reviewer` gates. Can be overridden per-project via `oat_auto_review_at_hill_checkpoints` in `plan.md` frontmatter. Legacy `autoReviewAtCheckpoints` remains a fallback. |
128
- | `workflow.dispatchPolicy.mode` | `string` | unset | Dispatch policy mode: `managed` lets OAT select model/effort controls; `inherit` leaves controls to the host/provider defaults. |
129
- | `workflow.dispatchPolicy.policy` | `string` | unset | Managed dispatch policy: `economy`, `balanced`, `high`, `frontier`, or `uncapped`. Capped policies compile to provider targets; `uncapped` keeps OAT-managed preferred selection without provider caps. `inherit` mode is separate and leaves controls to the host/provider. |
130
- | `workflow.dispatchCeiling.preset` | `string` | unset | Legacy compatibility preset for capped managed policy setup (`balanced`, `maximum`, `cost-conscious`). `maximum` maps to `high`; `cost-conscious` maps to `economy`. |
131
- | `workflow.dispatchCeiling.providers.<provider>` | `object` or legacy scalar | unset | Reusable provider candidate ladder. The canonical shape maps `economy`, `balanced`, `high`, and `frontier` to ordered candidate cells; project and phase state record only a named maximum over that ladder. |
132
- | `workflow.dispatchCeiling.providers.<provider>.<tier>` | `{ candidates: [...] }` | unset | One ordered candidate cell. Codex entries carry exact `model` plus `effort` and resolve to materialized roles; Claude entries carry a model; Cursor strings remain opaque and pass to Cursor byte-for-byte. The final candidate defines that tier's reviewer ceiling. |
133
- | `workflow.dispatchCeiling.providers.{codex,claude,cursor}` | `string`, route, or target | unset | Legacy compatibility input normalized to a one-candidate ladder. New configuration should use tiered candidate cells. The flat keys `workflow.dispatchCeiling.codex` and `workflow.dispatchCeiling.claude` were removed without migration. |
134
- | `workflow.gates.skills` | `object` | unset | Per-skill final gate config keyed by skill name. Managed with `oat gate set/unset`; gate-aware skills declare `oat_gateable: true`. |
135
- | `workflow.gates.execTargets` | `object` | built-ins | Cross-runtime exec target registry keyed by opaque target id. Managed with `oat gate target set/unset`; built-ins cover Codex, Claude, and Cursor defaults. |
136
- | `archive.s3Uri` | `string` | - | Base S3 URI for repo-scoped archived project sync, for example `s3://bucket/oat-archive` |
137
- | `archive.s3SyncOnComplete` | `boolean` | `false` | When `true`, `oat-project-complete` uploads the archived project to the configured S3 archive after local archive succeeds |
138
- | `archive.summaryExportPath` | `string` | - | Repo-relative directory where completion exports `summary.md` as a dated snapshot like `20260401-<project-name>.md` for durable tracked reference |
139
- | `archive.wrapUpExportPath` | `string` | - | Repo-relative directory where `oat-wrap-up` writes dated reports like `20260413-wrap-up-past-week.md`; when unset, the skill falls back to `.oat/repo/reference/wrap-ups/` |
140
- | `archive.awsProfile` | `string` | - | Optional AWS named profile forwarded as `AWS_PROFILE` to every `aws` invocation in archive flows (`oat-project-complete` S3 sync, `oat repo archive sync`). Overrides ambient shell `AWS_PROFILE` / `AWS_DEFAULT_PROFILE` when set. |
141
- | `archive.awsRegion` | `string` | - | Optional AWS region forwarded as `AWS_REGION` to every `aws` invocation in archive flows. Overrides ambient shell `AWS_REGION` / `AWS_DEFAULT_REGION` when set. |
142
-
143
- All `documentation.*` keys are managed via `oat config get/set` and are set automatically by `oat docs init`.
113
+ | Key | Type | Default | Description |
114
+ | ---------------------------------------------------------- | -------------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
115
+ | `version` | `number` | `1` | Schema version |
116
+ | `worktrees.root` | `string` | `".worktrees"` | Root directory for git worktrees (repo-relative or absolute) |
117
+ | `projects.root` | `string` | `".oat/projects/shared"` | Default root directory for OAT projects |
118
+ | `projects.defaultScope` | `string` | `"synced"` | Scope used by project creation when `--scope` is omitted: `shared`, `local`, or `synced`. Environment override: `OAT_PROJECTS_DEFAULT_SCOPE`. |
119
+ | `localPaths` | `string[]` | - | Gitignored directories to sync between main repo and worktrees. Supports glob patterns. Managed via `oat local add/remove`. |
120
+ | `documentation.root` | `string` | - | Docs **app root** — the directory `oat docs init` scaffolds (e.g., `apps/docs`). Authored pages live under `<root>/docs`. See [Documentation path resolution](#documentation-path-resolution). |
121
+ | `documentation.tooling` | `string` | - | Documentation framework identifier (`mkdocs` or `fumadocs`) |
122
+ | `documentation.config` | `string` | - | Path to the documentation framework config file (e.g., `mkdocs.yml`, `next.config.js`) |
123
+ | `documentation.index` | `string` | - | Path to the docs surface entry point: the generated app-root manifest `<root>/index.md` for Fumadocs, `mkdocs.yml` for MkDocs. Set by `oat docs init`. `oat docs generate-index` writes it only for the Fumadocs manifest transition and never for MkDocs. |
124
+ | `documentation.excludes` | `string[]` | - | Globs, relative to the docs directory, that `oat docs generate-index` leaves out of the generated index. See [Excluding pages from the generated index](#excluding-pages-from-the-generated-index). |
125
+ | `documentation.instructionPointerExcludes` | `string[]` | - | Repo-relative directories `oat instructions sync`/`validate` must not treat as pointer sites, additive to the documentation content root they skip by default. Normalized and de-duplicated; a malformed value (non-array, or an empty or non-string entry) is rejected with exit code `2`. Set as one comma-separated value with `oat config set`; absolute paths and paths escaping the repository are refused. See [Instruction Sync](../provider-sync/instruction-sync.md#documentation-trees). |
126
+ | `documentation.requireForProjectCompletion` | `boolean` | `false` | When `true`, OAT project completion gates require documentation to be updated |
127
+ | `git.defaultBranch` | `string` | `"main"` | Default branch for PR creation. Auto-detected during `oat init` via `gh repo view` or `origin/HEAD`. Used by `oat-project-pr-final` and `oat-project-pr-progress`. |
128
+ | `workflow.autoReviewAtHillCheckpoints` | `boolean` | unset | When `true`, completing a HiLL checkpoint automatically runs the extra lifecycle review. Does not control Tier 1 per-phase `oat-reviewer` gates. Can be overridden per-project via `oat_auto_review_at_hill_checkpoints` in `plan.md` frontmatter. Legacy `autoReviewAtCheckpoints` remains a fallback. |
129
+ | `workflow.dispatchPolicy.mode` | `string` | unset | Dispatch policy mode: `managed` lets OAT select model/effort controls; `inherit` leaves controls to the host/provider defaults. |
130
+ | `workflow.dispatchPolicy.policy` | `string` | unset | Managed dispatch policy: `economy`, `balanced`, `high`, `frontier`, or `uncapped`. Capped policies compile to provider targets; `uncapped` keeps OAT-managed preferred selection without provider caps. `inherit` mode is separate and leaves controls to the host/provider. |
131
+ | `workflow.dispatchCeiling.preset` | `string` | unset | Legacy compatibility preset for capped managed policy setup (`balanced`, `maximum`, `cost-conscious`). `maximum` maps to `high`; `cost-conscious` maps to `economy`. |
132
+ | `workflow.dispatchCeiling.providers.<provider>` | `object` or legacy scalar | unset | Reusable provider candidate ladder. The canonical shape maps `economy`, `balanced`, `high`, and `frontier` to ordered candidate cells; project and phase state record only a named maximum over that ladder. |
133
+ | `workflow.dispatchCeiling.providers.<provider>.<tier>` | `{ candidates: [...] }` | unset | One ordered candidate cell. Codex entries carry exact `model` plus `effort` and resolve to materialized roles; Claude entries carry a model; Cursor strings remain opaque and pass to Cursor byte-for-byte. The final candidate defines that tier's reviewer ceiling. |
134
+ | `workflow.dispatchCeiling.providers.{codex,claude,cursor}` | `string`, route, or target | unset | Legacy compatibility input normalized to a one-candidate ladder. New configuration should use tiered candidate cells. The flat keys `workflow.dispatchCeiling.codex` and `workflow.dispatchCeiling.claude` were removed without migration. |
135
+ | `workflow.gates.skills` | `object` | unset | Per-skill final gate config keyed by skill name. Managed with `oat gate set/unset`; gate-aware skills declare `oat_gateable: true`. |
136
+ | `workflow.gates.execTargets` | `object` | built-ins | Cross-runtime exec target registry keyed by opaque target id. Managed with `oat gate target set/unset`; built-ins cover Codex, Claude, and Cursor defaults. |
137
+ | `archive.s3Uri` | `string` | - | Base S3 URI for repo-scoped archived project sync, for example `s3://bucket/oat-archive` |
138
+ | `archive.s3SyncOnComplete` | `boolean` | `false` | When `true`, `oat-project-complete` uploads the archived project to the configured S3 archive after local archive succeeds |
139
+ | `archive.summaryExportPath` | `string` | - | Repo-relative directory where completion exports `summary.md` as a dated snapshot like `20260401-<project-name>.md` for durable tracked reference |
140
+ | `archive.wrapUpExportPath` | `string` | - | Repo-relative directory where `oat-wrap-up` writes dated reports like `20260413-wrap-up-past-week.md`; when unset, the skill falls back to `.oat/repo/reference/wrap-ups/` |
141
+ | `archive.awsProfile` | `string` | - | Optional AWS named profile forwarded as `AWS_PROFILE` to every `aws` invocation in archive flows (`oat-project-complete` S3 sync, `oat repo archive sync`). Overrides ambient shell `AWS_PROFILE` / `AWS_DEFAULT_PROFILE` when set. |
142
+ | `archive.awsRegion` | `string` | - | Optional AWS region forwarded as `AWS_REGION` to every `aws` invocation in archive flows. Overrides ambient shell `AWS_REGION` / `AWS_DEFAULT_REGION` when set. |
143
+
144
+ Most `documentation.*` keys are managed via `oat config get/set` and are set automatically by `oat docs init`. One has no `oat config set` entry and is edited directly in `.oat/config.json`: `documentation.index`, which `oat docs init` still seeds for you.
144
145
  The `git.defaultBranch` key is auto-detected during `oat init` and can be overridden via `oat config set git.defaultBranch <branch>`.
145
146
  Archive settings are managed via `oat config get/set`, and `oat config describe archive.s3Uri` (or the other archive keys) shows the lifecycle and ownership details from the CLI.
146
147
  Workflow gate objects are structured config and are managed with `oat gate`, not the scalar `oat config set` surface. See [Workflow Gates](../cli-utilities/workflow-gates.md).