@open-agent-toolkit/cli 0.1.45 → 0.1.48

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 (112) hide show
  1. package/assets/agents/oat-phase-implementer.md +202 -191
  2. package/assets/agents/oat-reviewer.md +12 -2
  3. package/assets/config/dispatch-matrix-recommendation.json +120 -13
  4. package/assets/docs/cli-utilities/config-and-local-state.md +7 -0
  5. package/assets/docs/cli-utilities/configuration.md +183 -84
  6. package/assets/docs/cli-utilities/workflow-gates.md +161 -27
  7. package/assets/docs/contributing/skills.md +14 -8
  8. package/assets/docs/provider-sync/config.md +5 -1
  9. package/assets/docs/provider-sync/manifest-and-drift.md +6 -1
  10. package/assets/docs/provider-sync/providers.md +42 -5
  11. package/assets/docs/provider-sync/scope-and-surface.md +3 -2
  12. package/assets/docs/reference/cli-reference.md +3 -1
  13. package/assets/docs/reference/oat-directory-structure.md +27 -26
  14. package/assets/docs/workflows/projects/artifacts.md +31 -1
  15. package/assets/docs/workflows/projects/dispatch-ceiling.md +235 -174
  16. package/assets/docs/workflows/projects/implementation-execution.md +283 -253
  17. package/assets/docs/workflows/projects/lifecycle.md +26 -5
  18. package/assets/docs/workflows/projects/reviews.md +27 -2
  19. package/assets/migration/pjm-restructure.md +1 -1
  20. package/assets/public-package-versions.json +4 -4
  21. package/assets/skills/oat-project-implement/SKILL.md +347 -178
  22. package/assets/skills/oat-project-import-plan/SKILL.md +173 -16
  23. package/assets/skills/oat-project-next/SKILL.md +2 -2
  24. package/assets/skills/oat-project-plan/SKILL.md +122 -92
  25. package/assets/skills/oat-project-plan-writing/SKILL.md +246 -15
  26. package/assets/skills/oat-project-quick-start/SKILL.md +156 -94
  27. package/assets/skills/oat-project-review-provide/SKILL.md +94 -22
  28. package/dist/commands/config/index.d.ts +2 -2
  29. package/dist/commands/config/index.d.ts.map +1 -1
  30. package/dist/commands/config/index.js +129 -53
  31. package/dist/commands/doctor/index.d.ts +2 -2
  32. package/dist/commands/doctor/index.d.ts.map +1 -1
  33. package/dist/commands/doctor/index.js +102 -31
  34. package/dist/commands/gate/index.d.ts +7 -1
  35. package/dist/commands/gate/index.d.ts.map +1 -1
  36. package/dist/commands/gate/index.js +544 -67
  37. package/dist/commands/gate/review-verdict.d.ts +16 -0
  38. package/dist/commands/gate/review-verdict.d.ts.map +1 -1
  39. package/dist/commands/gate/review-verdict.js +72 -9
  40. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  41. package/dist/commands/project/dispatch-ceiling/index.js +586 -23
  42. package/dist/commands/providers/codex/index.d.ts +4 -0
  43. package/dist/commands/providers/codex/index.d.ts.map +1 -0
  44. package/dist/commands/providers/codex/index.js +7 -0
  45. package/dist/commands/providers/codex/materialize.d.ts +5 -0
  46. package/dist/commands/providers/codex/materialize.d.ts.map +1 -0
  47. package/dist/commands/providers/codex/materialize.js +157 -0
  48. package/dist/commands/providers/index.d.ts +2 -2
  49. package/dist/commands/providers/index.d.ts.map +1 -1
  50. package/dist/commands/providers/index.js +4 -2
  51. package/dist/commands/providers/providers.types.d.ts +23 -0
  52. package/dist/commands/providers/providers.types.d.ts.map +1 -1
  53. package/dist/commands/shared/codex-strays.d.ts.map +1 -1
  54. package/dist/commands/shared/codex-strays.js +11 -1
  55. package/dist/commands/shared/frontmatter.d.ts +4 -0
  56. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  57. package/dist/commands/shared/frontmatter.js +23 -0
  58. package/dist/commands/status/index.d.ts +5 -1
  59. package/dist/commands/status/index.d.ts.map +1 -1
  60. package/dist/commands/status/index.js +10 -4
  61. package/dist/commands/sync/index.d.ts.map +1 -1
  62. package/dist/commands/sync/index.js +10 -3
  63. package/dist/commands/sync/sync.types.d.ts +5 -1
  64. package/dist/commands/sync/sync.types.d.ts.map +1 -1
  65. package/dist/config/dispatch-ceiling-preset.d.ts +4 -0
  66. package/dist/config/dispatch-ceiling-preset.d.ts.map +1 -1
  67. package/dist/config/dispatch-ceiling-preset.js +3 -0
  68. package/dist/config/dispatch-policy-options.d.ts +20 -0
  69. package/dist/config/dispatch-policy-options.d.ts.map +1 -0
  70. package/dist/config/dispatch-policy-options.js +96 -0
  71. package/dist/config/oat-config.d.ts +25 -2
  72. package/dist/config/oat-config.d.ts.map +1 -1
  73. package/dist/config/oat-config.js +154 -24
  74. package/dist/config/resolve.d.ts +8 -0
  75. package/dist/config/resolve.d.ts.map +1 -1
  76. package/dist/config/resolve.js +66 -2
  77. package/dist/engine/index.d.ts +1 -1
  78. package/dist/engine/index.d.ts.map +1 -1
  79. package/dist/engine/index.js +1 -1
  80. package/dist/engine/scanner.d.ts +1 -0
  81. package/dist/engine/scanner.d.ts.map +1 -1
  82. package/dist/engine/scanner.js +17 -1
  83. package/dist/fs/paths.d.ts +4 -0
  84. package/dist/fs/paths.d.ts.map +1 -1
  85. package/dist/fs/paths.js +18 -1
  86. package/dist/providers/ceiling/registry.d.ts +8 -5
  87. package/dist/providers/ceiling/registry.d.ts.map +1 -1
  88. package/dist/providers/ceiling/registry.js +25 -7
  89. package/dist/providers/codex/codec/catalog.d.ts +14 -0
  90. package/dist/providers/codex/codec/catalog.d.ts.map +1 -0
  91. package/dist/providers/codex/codec/catalog.js +21 -0
  92. package/dist/providers/codex/codec/config-merge.d.ts +6 -0
  93. package/dist/providers/codex/codec/config-merge.d.ts.map +1 -1
  94. package/dist/providers/codex/codec/config-merge.js +7 -0
  95. package/dist/providers/codex/codec/materialize.d.ts +16 -0
  96. package/dist/providers/codex/codec/materialize.d.ts.map +1 -0
  97. package/dist/providers/codex/codec/materialize.js +58 -0
  98. package/dist/providers/codex/codec/shared.d.ts +20 -0
  99. package/dist/providers/codex/codec/shared.d.ts.map +1 -1
  100. package/dist/providers/codex/codec/shared.js +107 -6
  101. package/dist/providers/codex/codec/sync-extension.d.ts +7 -1
  102. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  103. package/dist/providers/codex/codec/sync-extension.js +299 -52
  104. package/dist/providers/identity/availability.d.ts +24 -1
  105. package/dist/providers/identity/availability.d.ts.map +1 -1
  106. package/dist/providers/identity/availability.js +253 -19
  107. package/dist/providers/identity/stamp.d.ts.map +1 -1
  108. package/dist/providers/identity/stamp.js +4 -0
  109. package/dist/shared/types.d.ts +1 -0
  110. package/dist/shared/types.d.ts.map +1 -1
  111. package/dist/shared/types.js +4 -0
  112. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: oat-project-review-provide
3
- version: 1.3.10
3
+ version: 1.3.12
4
4
  description: Use when the user explicitly asks to review an OAT project — e.g. "review project", "review the project", "run project review", or confirms a previously offered review. Do NOT auto-invoke on completed work alone. Resolves a project review scope and offers before running.
5
5
  disable-model-invocation: false
6
6
  user-invocable: true
@@ -31,7 +31,7 @@ Before acting, verify that there is an active OAT project or a user-provided rev
31
31
 
32
32
  When the invocation is manual, summarize the inferred review type and scope, then ask before running the review.
33
33
 
34
- **Gate-originated mode:** If the request context says the review is gate-originated, set `REVIEW_INVOCATION=gate`, honor any provided review type/scope arguments, and run without interactive confirmation prompts. The gate session is already the explicit authorization boundary. If subagent dispatch is unavailable, run the review inline in the current gate session instead of handing off to a fresh session.
34
+ **Gate-originated mode:** If the request context says the review is gate-originated, set `REVIEW_INVOCATION=gate`, honor any provided review type/scope arguments, and run without interactive confirmation prompts. The gate session is already the explicit authorization boundary, but it does not waive dispatch controls. Resolve the reviewer target and use the exact registered role or explicitly pinned fresh child. Inline gate review is allowed only with verified equivalent current-host model and effort controls or a documented base-role exception; otherwise fail closed.
35
35
 
36
36
  ## Mode Assertion
37
37
 
@@ -428,23 +428,30 @@ Proceed with review?
428
428
 
429
429
  If `REVIEW_INVOCATION=gate`, print the same scope summary but do not ask `Proceed with review?`; continue directly.
430
430
 
431
- ### Step 4.1: Dispatch Profile Override Advisory (Artifact Plan Only)
431
+ ### Step 4.1: Dispatch Profile Ceiling Advisory (Artifact Plan Only)
432
432
 
433
- When reviewing `artifact plan`, apply this Dispatch Profile override advisory:
433
+ When reviewing `artifact plan`, apply this Dispatch Profile named-ceiling
434
+ advisory:
434
435
 
435
436
  - A missing `## Dispatch Profile` section is normal and must not be flagged.
436
437
  - Important findings:
437
438
  - invalid phase ID that does not match a real plan phase
438
- - unknown active-provider tier value
439
- - low-tier override for multi-file integration, architecture, or review-heavy work
440
- - low-tier override with missing or generic rationale
439
+ - unknown named ceiling or a phase ceiling above the project ceiling
440
+ - wording that pins an exact provider model, family, effort, or role instead
441
+ of a named maximum
442
+ - low named ceiling for multi-file integration, architecture, or review-heavy
443
+ work
444
+ - low named ceiling with missing or generic rationale
441
445
  - Medium findings:
442
446
  - malformed but recoverable Dispatch Profile table structure
443
- - mid-tier override for architecture-heavy work without convincing rationale
447
+ - mid-tier ceiling for architecture-heavy work without convincing rationale
444
448
  - Minor findings:
445
449
  - rationale is present but weakly tied to phase scope
446
450
 
447
- Include this advisory in the Review Scope metadata for artifact plan reviews so the reviewer evaluates explicit override rows without treating omitted rows as a gap.
451
+ Include this advisory in the Review Scope metadata for artifact plan reviews so
452
+ the reviewer evaluates explicit ceiling rows without treating omitted rows as a
453
+ gap. A named ceiling is a maximum; lower configured candidates remain eligible
454
+ for later task selection.
448
455
 
449
456
  ### Step 4.5: Gather Deferred Findings Ledger (Final Scope Only)
450
457
 
@@ -510,6 +517,56 @@ Build the "Review Scope" metadata for the reviewer:
510
517
 
511
518
  ### Step 6: Execute Review (3-Tier Capability Model)
512
519
 
520
+ **Step 6.0: Resolve the managed reviewer target**
521
+
522
+ Before capability-tier selection, resolve the same reviewer contract used by
523
+ plan artifact review and implementation phase/final review:
524
+
525
+ ```bash
526
+ oat project dispatch-ceiling resolve --provider "$ACTIVE_PROVIDER" --role reviewer --preflight --json
527
+ ```
528
+
529
+ The reviewer resolver selects the final candidate of the configured review
530
+ ceiling. Do not supply ephemeral implementer candidate requests for artifact,
531
+ phase, project, or final review. A lower candidate is allowed only when a
532
+ separate reviewed contract explicitly authorizes reviewer lowering and defines
533
+ its bounds; a Dispatch Profile row does not authorize it.
534
+
535
+ Resolve every concrete managed reviewer target before probing generic subagent
536
+ availability or selecting Tier 1/Tier 2/Tier 3. The concrete target takes
537
+ precedence over all availability, preference, timeout, fresh-session, and gate
538
+ fallbacks.
539
+ A concrete managed Codex target takes precedence over tier availability.
540
+ Build the actual provider invocation before reporting the target as enforced:
541
+
542
+ - Codex uses the exact registered reviewer role or variant from
543
+ `providers.codex.dispatchArgs.variant` when selectable. If the current host
544
+ cannot select it, launch a fresh Codex child with the resolver target's
545
+ explicit model, reasoning effort, canonical role instructions from
546
+ `.agents/agents/oat-reviewer.md`, and the same Review Scope payload.
547
+ If that fresh child is unavailable, use only a verified-equivalent inline
548
+ route or block the review.
549
+ - Claude requires a non-empty `providers.claude.dispatchArgs.model`; the actual
550
+ provider invocation must include that exact value as its `model` argument.
551
+ - Cursor requires a non-empty `providers.cursor.dispatchArgs.model`; the actual
552
+ provider invocation must include that exact opaque string as its `model`
553
+ argument without normalization.
554
+
555
+ Managed incomplete resolver results, including a missing or incomplete
556
+ candidate ladder, fail closed before review. Route interactive repair through
557
+ the planning workflow's `Complete Dispatch Ladder Adoption Contract`; do not
558
+ invent a reviewer target.
559
+
560
+ On timeout or retry, reuse the same exact role or complete invocation payload,
561
+ including the Claude or Cursor model argument. If the host cannot apply a
562
+ required role or model argument, fail closed or block unless inline execution
563
+ has verified equivalent current-host controls.
564
+ Workflow correctness must not require provider restart or hot reload.
565
+ Never use a managed base role because a target is missing or unavailable; a
566
+ managed base role is forbidden except for
567
+ explicit inherit/default behavior or the documented managed-uncapped reviewer
568
+ fallback.
569
+
513
570
  **Step 6a: Probe Subagent Availability**
514
571
 
515
572
  Before selecting a tier, announce the probe and its result so the user can see what's happening:
@@ -526,8 +583,7 @@ Detection logic:
526
583
  - If the host is Cursor, invoke `oat-reviewer` using Cursor-native explicit invocation (`/oat-reviewer`) or natural mention, and resolve from `.cursor/agents/oat-reviewer.md` (or `.claude/agents/oat-reviewer.md` compatibility path).
527
584
  - If the host is Codex multi-agent, verify Codex requirements first:
528
585
  - `[features] multi_agent = true` is enabled in active Codex config.
529
- - If explicit role pinning is desired, `agent_type` must be a built-in role (`default`/`worker`/`explorer`) or a custom role declared under `[agents.<name>]`.
530
- - Codex may also auto-select and spawn agents without explicit role pinning.
586
+ - For a concrete managed target, `agent_type` must be the exact custom role declared under `[agents.<name>]`; built-in roles and auto-selection are not equivalent fallbacks.
531
587
  - If the current Codex host requires explicit user authorization before calling `spawn_agent`, do not mark `oat-reviewer` as unresolved. Announce `authorization required` and ask one concise confirmation question before selecting Tier 2 or Tier 3:
532
588
 
533
589
  ```
@@ -535,12 +591,12 @@ Detection logic:
535
591
  ```
536
592
 
537
593
  - If the user authorizes delegation and Codex role prerequisites are satisfied, use **Tier 1**.
538
- - If the user declines delegation, continue with the existing Tier 2 / Tier 3 fallback flow.
594
+ - If the user declines delegation, continue only through a target-preserving pinned-child or guarded inline route. Otherwise block.
539
595
 
540
596
  - If the runtime can dispatch reviewer work (`subagent_type` in Claude Code, Cursor invocation via `/name` or natural mention, or Codex multi-agent spawn/auto-spawn) → **Tier 1**.
541
- - If the Task tool is not available or subagent dispatch is not supported **Tier 2**.
542
- - If user explicitly requests inline or confirms they are already in a fresh session **Tier 3**.
543
- - If `REVIEW_INVOCATION=gate` and Tier 1 is unavailable, skip Tier 2 and use **Tier 3** inline. Do not return fresh-session instructions from a gate invocation.
597
+ - If the Task tool is not available or subagent dispatch is not supported, use **Tier 2** only after applying the target-first contract.
598
+ - If the user explicitly requests inline or confirms they are already in a fresh session, use **Tier 3** only when the guarded inline route is valid.
599
+ - Gate-originated review skips fresh-session handoff instructions and immediately uses the first target-preserving route available from Step 6.0. If none exists, fail closed.
544
600
 
545
601
  **Step 6b: Tier 1 — Subagent (if available)**
546
602
 
@@ -549,9 +605,9 @@ First, pre-compute the review artifact path using Step 7 naming conventions so i
549
605
  Then spawn the reviewer:
550
606
 
551
607
  - Use provider-appropriate dispatch:
552
- - Claude Code: Task tool with `subagent_type: "oat-reviewer"` (resolves from `.claude/agents/oat-reviewer.md`).
553
- - Cursor: explicit invocation `/oat-reviewer` (or natural mention) with agent resolved from `.cursor/agents/oat-reviewer.md` or `.claude/agents/oat-reviewer.md` compatibility path.
554
- - Codex style: ask Codex to spawn agent(s) for review work and wait for all results; optionally pin `agent_type` when a specific built-in/custom role is required.
608
+ - Claude Code: Task tool with `subagent_type: "oat-reviewer"` (resolves from `.claude/agents/oat-reviewer.md`). For a concrete managed target, the payload must also contain `model: providers.claude.dispatchArgs.model` with the resolver-returned value.
609
+ - Cursor: explicit invocation `/oat-reviewer` (or natural mention) with agent resolved from `.cursor/agents/oat-reviewer.md` or `.claude/agents/oat-reviewer.md` compatibility path. For a concrete managed target, the invocation must also contain `model: providers.cursor.dispatchArgs.model` with the exact opaque resolver-returned string.
610
+ - Codex style: for a concrete managed target, spawn the exact resolver-returned `agent_type`; if it cannot be selected, use the explicitly pinned fresh-child route from Step 6.0. Generic auto-selection is permitted only for the documented base-role exceptions.
555
611
  - Pass the Review Scope metadata block from Step 5 as the prompt
556
612
  - Include the pre-computed artifact path for the subagent to write to
557
613
  - **If a worktree was resolved in Step 1.5:** include the worktree path in the prompt so the subagent writes the artifact to the worktree directory, not the current session's working directory
@@ -566,12 +622,12 @@ After the subagent completes:
566
622
 
567
623
  **Step 6c: Tier 2 — Fresh Session (recommended fallback)**
568
624
 
569
- If subagent not available:
625
+ If target-preserving subagent dispatch is not available:
570
626
 
571
627
  - If user is already in a fresh session (confirmed), proceed to Tier 3.
572
628
  - If Codex reported `authorization required` and the user approved delegation, do **not** use Tier 2. Return to Tier 1 and delegate to `oat-reviewer`.
573
629
  - If user prefers fresh session: provide instructions and exit.
574
- - If `REVIEW_INVOCATION=gate`: do not provide fresh-session instructions; run Tier 3 inline instead.
630
+ - Gate-originated review does not return fresh-session instructions; it uses the guarded route selected in Step 6.0 or fails closed.
575
631
 
576
632
  Instructions for fresh session:
577
633
 
@@ -585,7 +641,13 @@ To run review in a fresh session:
585
641
 
586
642
  **Step 6d: Tier 3 — Inline Reset (fallback)**
587
643
 
588
- If user insists on inline review in current session:
644
+ If the user requests inline review, first verify equivalent current-host model
645
+ and effort controls. Inline is also allowed for explicit inherit/default or the
646
+ documented managed-uncapped reviewer behavior. User preference alone does not
647
+ override a concrete managed target; if the guard fails, use the exact/pinned
648
+ route or block.
649
+
650
+ When inline is allowed:
589
651
 
590
652
  - Run "reset protocol":
591
653
  1. Re-read required artifacts for current workflow mode from scratch
@@ -649,6 +711,13 @@ oat_review_scope: { scope }
649
711
  oat_review_type: { code|artifact }
650
712
  oat_review_invocation: { manual|auto|gate }
651
713
  oat_project: { PROJECT_PATH }
714
+ # Gate-only: copy the exact prompt-provided fields below.
715
+ oat_gate_run_id: { gate run id }
716
+ oat_gate_target: { configured target id }
717
+ oat_gate_runtime: { configured runtime }
718
+ oat_invocation_model: { configured model|provider-default|unknown }
719
+ oat_invocation_reasoning_effort: { configured effort|provider-default|unknown }
720
+ oat_invocation_source: { exec-target-config|unknown }
652
721
  ---
653
722
 
654
723
  # {Code|Artifact} Review: {scope}
@@ -669,6 +738,8 @@ When `oat-project-implement` spawns this skill for auto-review at checkpoints, i
669
738
 
670
739
  When `oat gate review` invokes this skill, it includes gate-originated context instructing the reviewer to write `oat_review_invocation: gate`. Honor that instruction in the artifact frontmatter.
671
740
 
741
+ The gate prompt also supplies exact values for `oat_gate_run_id`, `oat_gate_target`, `oat_gate_runtime`, `oat_invocation_model`, `oat_invocation_reasoning_effort`, and `oat_invocation_source`. Copy all six values verbatim for gate-originated artifacts. These fields record OAT's configured invocation; do not derive them from `baseCommand`, the target id, model self-identification, or surrounding dispatch prose. Optional observed/self-reported identity is separate and non-authoritative.
742
+
672
743
  For all other invocations (user-triggered, fresh session), use `manual`.
673
744
 
674
745
  Gate parsing contract:
@@ -784,7 +855,8 @@ For best review quality, run in a fresh session:
784
855
  2. Run the oat-project-review-provide skill with: code {scope}
785
856
  3. Return here and run the oat-project-review-receive skill
786
857
 
787
- Or say "inline" to run review in current session (less reliable).
858
+ Or say "inline" to request review in the current session; the managed-target
859
+ guard still applies.
788
860
 
789
861
  ```
790
862
 
@@ -2,7 +2,7 @@ import { buildCommandContext, type CommandContext } from '../../app/command-cont
2
2
  import { type PromptContext } from '../shared/shared.prompts.js';
3
3
  import { type OatConfig, type OatLocalConfig, type UserConfig } from '../../config/oat-config.js';
4
4
  import { type ResolvedConfig } from '../../config/resolve.js';
5
- import { type MatrixCellAvailability, type ValidateMatrixCellOptions } from '../../providers/identity/availability.js';
5
+ import { type MatrixCellAvailabilityResponse, type ValidateMatrixCellOptions } from '../../providers/identity/availability.js';
6
6
  import { Command } from 'commander';
7
7
  interface ConfigCommandDependencies {
8
8
  buildCommandContext: (options: Parameters<typeof buildCommandContext>[0]) => CommandContext;
@@ -18,7 +18,7 @@ interface ConfigCommandDependencies {
18
18
  resolveAssetsRoot: () => Promise<string>;
19
19
  readFile: (path: string) => Promise<string>;
20
20
  confirmAction: (message: string, ctx: PromptContext) => Promise<boolean>;
21
- validateMatrixCell: (provider: string, value: string, options: ValidateMatrixCellOptions) => Promise<MatrixCellAvailability>;
21
+ validateMatrixCell: (provider: string, value: string, options: ValidateMatrixCellOptions) => Promise<MatrixCellAvailabilityResponse>;
22
22
  processEnv: NodeJS.ProcessEnv;
23
23
  }
24
24
  export declare function createConfigCommand(overrides?: Partial<ConfigCommandDependencies>): Command;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EACL,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAchB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkFpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AA61DD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAwKT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAQzC,OAAO,EAML,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAgBhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkFpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AA45DD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAsKT"}
@@ -5,11 +5,12 @@ import { resolveProjectsRoot } from '../shared/oat-paths.js';
5
5
  import { confirmAction, } from '../shared/shared.prompts.js';
6
6
  import { readGlobalOptions } from '../shared/shared.utils.js';
7
7
  import { compileDispatchCeilingPreset } from '../../config/dispatch-ceiling-preset.js';
8
- import { readOatConfig, readOatLocalConfig, readUserConfig, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
8
+ import { dispatchPolicyModeDescription, dispatchPolicyPolicyDescription, managedDispatchPolicyValueList, } from '../../config/dispatch-policy-options.js';
9
+ import { VALID_DISPATCH_POLICY_MODES, VALID_MANAGED_DISPATCH_POLICIES, isCodexMaterializedRouteTarget, isWorkflowDispatchCandidateLadder, isWorkflowDispatchFallbackRoute, readOatConfig, readOatLocalConfig, readUserConfig, validateDispatchRouteTarget, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
9
10
  import { resolveEffectiveConfig, } from '../../config/resolve.js';
10
11
  import { resolveAssetsRoot } from '../../fs/assets.js';
11
12
  import { resolveProjectRoot } from '../../fs/paths.js';
12
- import { validateMatrixCell, } from '../../providers/identity/availability.js';
13
+ import { normalizeMatrixCellAvailability, validateMatrixCell, } from '../../providers/identity/availability.js';
13
14
  import { Command } from 'commander';
14
15
  import { createConfigDumpCommand } from './dump.js';
15
16
  const DISPATCH_CEILING_PROVIDER_KEY_PREFIX = 'workflow.dispatchCeiling.providers.';
@@ -478,7 +479,7 @@ const CONFIG_CATALOG = [
478
479
  defaultValue: 'unset',
479
480
  mutability: 'read/write',
480
481
  owningCommand: 'oat config set workflow.dispatchPolicy.mode <value>',
481
- description: 'Dispatch policy mode. "managed" means OAT selects model/effort from workflow.dispatchPolicy.policy; "inherit" means OAT leaves dispatch controls to the host/provider defaults. Set workflow.dispatchPolicy.policy to choose a managed policy. Resolution: local > shared > user > default.',
482
+ description: dispatchPolicyModeDescription(),
482
483
  },
483
484
  {
484
485
  key: 'workflow.dispatchPolicy.policy',
@@ -489,14 +490,14 @@ const CONFIG_CATALOG = [
489
490
  defaultValue: 'unset',
490
491
  mutability: 'read/write',
491
492
  owningCommand: 'oat config set workflow.dispatchPolicy.policy <value>',
492
- description: 'Managed dispatch policy. economy, balanced, high, and frontier are capped managed policies; uncapped keeps OAT-managed preferred selection without provider caps. Setting this key writes workflow.dispatchPolicy.mode=managed. Resolution: local > shared > user > default.',
493
+ description: dispatchPolicyPolicyDescription(),
493
494
  },
494
495
  {
495
496
  key: 'workflow.dispatchCeiling.providers.codex',
496
497
  group: 'Workflow Preferences (3-layer: local > shared > user)',
497
498
  file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
498
499
  scope: 'workflow',
499
- type: 'low | medium | high | xhigh',
500
+ type: 'low | medium | high | xhigh | max',
500
501
  defaultValue: 'unset',
501
502
  mutability: 'read/write',
502
503
  owningCommand: 'oat config set workflow.dispatchCeiling.providers.codex <value>',
@@ -614,20 +615,15 @@ const WORKFLOW_ENUM_VALUES = {
614
615
  'workflow.postImplementSequence': ['wait', 'summary', 'pr', 'docs-pr'],
615
616
  'workflow.reviewExecutionModel': ['subagent', 'inline', 'fresh-session'],
616
617
  'workflow.designMode': ['collaborative', 'selective', 'draft'],
617
- 'workflow.dispatchPolicy.mode': ['managed', 'inherit'],
618
- 'workflow.dispatchPolicy.policy': [
619
- 'economy',
620
- 'balanced',
621
- 'high',
622
- 'frontier',
623
- 'uncapped',
624
- ],
618
+ 'workflow.dispatchPolicy.mode': [...VALID_DISPATCH_POLICY_MODES],
619
+ 'workflow.dispatchPolicy.policy': [...VALID_MANAGED_DISPATCH_POLICIES],
625
620
  'workflow.dispatchCeiling.preset': ['balanced', 'maximum', 'cost-conscious'],
626
621
  'workflow.dispatchCeiling.providers.codex': [
627
622
  'low',
628
623
  'medium',
629
624
  'high',
630
625
  'xhigh',
626
+ 'max',
631
627
  ],
632
628
  'workflow.dispatchCeiling.providers.claude': [
633
629
  'haiku',
@@ -747,13 +743,15 @@ function isRecord(value) {
747
743
  return typeof value === 'object' && value !== null && !Array.isArray(value);
748
744
  }
749
745
  function matrixCellAvailabilityWarning(path, value, availability) {
750
- if (availability === 'valid') {
746
+ const result = normalizeMatrixCellAvailability(availability);
747
+ if (result.availability === 'valid') {
751
748
  return null;
752
749
  }
753
- if (availability === 'unknown-value') {
754
- return `${path} value '${value}' was not recognized by the provider availability oracle; saving anyway.`;
750
+ const details = result.message ? ` ${result.message}` : '';
751
+ if (result.availability === 'unknown-value') {
752
+ return `${path} value '${value}' was not recognized by the provider availability oracle; saving anyway.${details}`;
755
753
  }
756
- return `${path} value '${value}' could not be validated because the provider availability oracle is unavailable; saving anyway.`;
754
+ return `${path} value '${value}' could not be validated because the provider availability oracle is unavailable; saving anyway.${details}`;
757
755
  }
758
756
  function parseDispatchMatrixRecommendation(raw) {
759
757
  const parsed = JSON.parse(raw);
@@ -775,26 +773,64 @@ function parseDispatchMatrixRecommendation(raw) {
775
773
  function isRouteTarget(entry) {
776
774
  return isRecord(entry);
777
775
  }
776
+ function formatRouteTargetValue(entry) {
777
+ return [entry.model, entry.effort].filter(Boolean).join('/');
778
+ }
778
779
  function addDispatchMatrixCellRefs(refs, provider, path, cell) {
779
780
  if (typeof cell === 'string') {
780
781
  refs.push({ provider, value: cell, path });
781
782
  return;
782
783
  }
783
- for (const [index, entry] of cell.entries()) {
784
- const entryPath = `${path}[${index}]`;
784
+ const addEntry = (entry, entryPath) => {
785
785
  if (typeof entry === 'string') {
786
786
  refs.push({ provider, value: entry, path: entryPath });
787
- continue;
787
+ return;
788
788
  }
789
789
  if (!isRouteTarget(entry)) {
790
- continue;
790
+ return;
791
+ }
792
+ const targetProvider = entry.harness ?? provider;
793
+ if (isCodexMaterializedRouteTarget(provider, entry)) {
794
+ if (entry.model && entry.effort) {
795
+ refs.push({
796
+ provider: targetProvider,
797
+ value: formatRouteTargetValue(entry),
798
+ path: entryPath,
799
+ target: entry,
800
+ });
801
+ return;
802
+ }
791
803
  }
792
804
  if (entry.model) {
793
- refs.push({ provider, value: entry.model, path: `${entryPath}.model` });
805
+ refs.push({
806
+ provider: targetProvider,
807
+ value: entry.model,
808
+ path: `${entryPath}.model`,
809
+ });
794
810
  }
795
811
  if (entry.effort) {
796
- refs.push({ provider, value: entry.effort, path: `${entryPath}.effort` });
812
+ refs.push({
813
+ provider: targetProvider,
814
+ value: entry.effort,
815
+ path: `${entryPath}.effort`,
816
+ });
797
817
  }
818
+ };
819
+ if (isWorkflowDispatchCandidateLadder(cell)) {
820
+ for (const [candidateIndex, candidate] of cell.candidates.entries()) {
821
+ const candidatePath = `${path}.candidates[${candidateIndex}]`;
822
+ if (isWorkflowDispatchFallbackRoute(candidate)) {
823
+ for (const [routeIndex, entry] of candidate.route.entries()) {
824
+ addEntry(entry, `${candidatePath}.route[${routeIndex}]`);
825
+ }
826
+ continue;
827
+ }
828
+ addEntry(candidate, candidatePath);
829
+ }
830
+ return;
831
+ }
832
+ for (const [index, entry] of cell.entries()) {
833
+ addEntry(entry, `${path}[${index}]`);
798
834
  }
799
835
  }
800
836
  function collectDispatchMatrixCellRefs(providers) {
@@ -814,6 +850,46 @@ function collectDispatchMatrixCellRefs(providers) {
814
850
  }
815
851
  return refs;
816
852
  }
853
+ function collectDispatchMatrixTargetValidationErrors(providers) {
854
+ const errors = [];
855
+ for (const [provider, providerValue] of Object.entries(providers)) {
856
+ if (typeof providerValue === 'string') {
857
+ continue;
858
+ }
859
+ const providerPath = `workflow.dispatchCeiling.providers.${provider}`;
860
+ for (const [tier, cell] of Object.entries(providerValue)) {
861
+ if (cell === undefined || typeof cell === 'string') {
862
+ continue;
863
+ }
864
+ const validateEntry = (entry, entryPath) => {
865
+ if (!isRouteTarget(entry)) {
866
+ return;
867
+ }
868
+ const validation = validateDispatchRouteTarget(provider, entry);
869
+ if (!validation.valid) {
870
+ errors.push(`${entryPath}: ${validation.reason}`);
871
+ }
872
+ };
873
+ if (isWorkflowDispatchCandidateLadder(cell)) {
874
+ for (const [candidateIndex, candidate] of cell.candidates.entries()) {
875
+ const candidatePath = `${providerPath}.${tier}.candidates[${candidateIndex}]`;
876
+ if (isWorkflowDispatchFallbackRoute(candidate)) {
877
+ for (const [routeIndex, entry] of candidate.route.entries()) {
878
+ validateEntry(entry, `${candidatePath}.route[${routeIndex}]`);
879
+ }
880
+ continue;
881
+ }
882
+ validateEntry(candidate, candidatePath);
883
+ }
884
+ continue;
885
+ }
886
+ for (const [index, entry] of cell.entries()) {
887
+ validateEntry(entry, `${providerPath}.${tier}[${index}]`);
888
+ }
889
+ }
890
+ }
891
+ return errors;
892
+ }
817
893
  function applyWorkflowValue(workflow, key, value) {
818
894
  const subKey = key.slice('workflow.'.length);
819
895
  if (subKey === 'dispatchPolicy.mode') {
@@ -826,7 +902,7 @@ function applyWorkflowValue(workflow, key, value) {
826
902
  }
827
903
  const policy = workflow.dispatchPolicy?.policy;
828
904
  if (!policy) {
829
- throw new Error('Cannot set workflow.dispatchPolicy.mode to managed without an existing workflow.dispatchPolicy.policy. Set workflow.dispatchPolicy.policy <economy|balanced|high|frontier|uncapped> instead.');
905
+ throw new Error(`Cannot set workflow.dispatchPolicy.mode to managed without an existing workflow.dispatchPolicy.policy. Set workflow.dispatchPolicy.policy <${managedDispatchPolicyValueList('|')}> instead.`);
830
906
  }
831
907
  return {
832
908
  ...workflow,
@@ -923,6 +999,9 @@ function formatResolvedValue(value) {
923
999
  if (Array.isArray(value)) {
924
1000
  return value.join(',');
925
1001
  }
1002
+ if (typeof value === 'object') {
1003
+ return JSON.stringify(value);
1004
+ }
926
1005
  return String(value);
927
1006
  }
928
1007
  async function getConfigValue(repoRoot, userConfigDir, key, dependencies) {
@@ -956,6 +1035,7 @@ async function setConfigValue(repoRoot, userConfigDir, key, rawValue, surface, d
956
1035
  const availability = await dependencies.validateMatrixCell(providerNameFromConfigKey(key), parsedValue, {
957
1036
  cwd: repoRoot,
958
1037
  env: dependencies.processEnv,
1038
+ detailed: true,
959
1039
  });
960
1040
  const warning = dispatchProviderAvailabilityWarning(key, parsedValue, availability);
961
1041
  if (warning) {
@@ -1139,19 +1219,19 @@ async function setConfigValue(repoRoot, userConfigDir, key, rawValue, surface, d
1139
1219
  source: 'shared',
1140
1220
  };
1141
1221
  }
1142
- function hasExistingDispatchMatrix(config) {
1143
- const providers = config.workflow?.dispatchCeiling?.providers;
1144
- return providers !== undefined && Object.keys(providers).length > 0;
1145
- }
1146
1222
  async function loadDispatchMatrixRecommendation(dependencies) {
1147
1223
  const assetsRoot = await dependencies.resolveAssetsRoot();
1148
1224
  const assetPath = join(assetsRoot, DISPATCH_MATRIX_RECOMMENDATION_ASSET);
1149
1225
  return parseDispatchMatrixRecommendation(await dependencies.readFile(assetPath));
1150
1226
  }
1151
1227
  async function validateRecommendationCells(repoRoot, recommendation, dependencies, warn) {
1228
+ const targetErrors = collectDispatchMatrixTargetValidationErrors(recommendation.providers);
1229
+ if (targetErrors.length > 0) {
1230
+ throw new Error(targetErrors.join('\n'));
1231
+ }
1152
1232
  for (const ref of collectDispatchMatrixCellRefs(recommendation.providers)) {
1153
1233
  const closedValues = closedDispatchProviderValues(ref.provider);
1154
- if (closedValues && !closedValues.includes(ref.value)) {
1234
+ if (!ref.target && closedValues && !closedValues.includes(ref.value)) {
1155
1235
  throw new Error(`Invalid value for ${ref.path}: expected one of ${closedValues.join(' | ')}, got '${ref.value}'`);
1156
1236
  }
1157
1237
  let availability;
@@ -1159,6 +1239,8 @@ async function validateRecommendationCells(repoRoot, recommendation, dependencie
1159
1239
  availability = await dependencies.validateMatrixCell(ref.provider, ref.value, {
1160
1240
  cwd: repoRoot,
1161
1241
  env: dependencies.processEnv,
1242
+ detailed: true,
1243
+ ...(ref.target ? { target: ref.target } : {}),
1162
1244
  });
1163
1245
  }
1164
1246
  catch {
@@ -1171,35 +1253,35 @@ async function validateRecommendationCells(repoRoot, recommendation, dependencie
1171
1253
  }
1172
1254
  }
1173
1255
  function applyDispatchMatrixRecommendation(workflow, recommendation) {
1256
+ const existingProviders = workflow?.dispatchCeiling?.providers ?? {};
1257
+ const providers = {
1258
+ ...recommendation.providers,
1259
+ };
1260
+ for (const [provider, existingValue] of Object.entries(existingProviders)) {
1261
+ const recommendedValue = recommendation.providers[provider];
1262
+ if (recommendedValue &&
1263
+ typeof recommendedValue !== 'string' &&
1264
+ typeof existingValue !== 'string') {
1265
+ providers[provider] = { ...recommendedValue, ...existingValue };
1266
+ }
1267
+ else {
1268
+ providers[provider] = existingValue;
1269
+ }
1270
+ }
1174
1271
  return {
1175
1272
  ...(workflow ?? {}),
1176
1273
  dispatchCeiling: {
1177
1274
  ...workflow?.dispatchCeiling,
1178
1275
  recommendationVersion: recommendation.version,
1179
- providers: recommendation.providers,
1276
+ providers,
1180
1277
  },
1181
1278
  };
1182
1279
  }
1183
- async function confirmDispatchMatrixOverwrite(context, dependencies, source, yes) {
1184
- if (yes) {
1185
- return;
1186
- }
1187
- if (!context.interactive) {
1188
- throw new Error('Dispatch matrix already exists; rerun interactively or pass --yes to replace it.');
1189
- }
1190
- const shouldReplace = await dependencies.confirmAction(`Replace existing dispatch matrix in ${source} config?`, { interactive: context.interactive });
1191
- if (!shouldReplace) {
1192
- throw new Error('Dispatch matrix adoption cancelled; existing matrix unchanged.');
1193
- }
1194
- }
1195
1280
  async function adoptDispatchMatrixRecommendation(repoRoot, userConfigDir, options, context, dependencies) {
1196
1281
  const source = options.surface === 'auto' ? 'local' : options.surface;
1197
1282
  const recommendation = await loadDispatchMatrixRecommendation(dependencies);
1198
1283
  if (source === 'user') {
1199
1284
  const userConfig = await dependencies.readUserConfig(userConfigDir);
1200
- if (hasExistingDispatchMatrix(userConfig)) {
1201
- await confirmDispatchMatrixOverwrite(context, dependencies, source, options.yes);
1202
- }
1203
1285
  await validateRecommendationCells(repoRoot, recommendation, dependencies, context.logger.warn);
1204
1286
  await dependencies.writeUserConfig(userConfigDir, {
1205
1287
  ...userConfig,
@@ -1213,9 +1295,6 @@ async function adoptDispatchMatrixRecommendation(repoRoot, userConfigDir, option
1213
1295
  }
1214
1296
  if (source === 'local') {
1215
1297
  const localConfig = await dependencies.readOatLocalConfig(repoRoot);
1216
- if (hasExistingDispatchMatrix(localConfig)) {
1217
- await confirmDispatchMatrixOverwrite(context, dependencies, source, options.yes);
1218
- }
1219
1298
  await validateRecommendationCells(repoRoot, recommendation, dependencies, context.logger.warn);
1220
1299
  await dependencies.writeOatLocalConfig(repoRoot, {
1221
1300
  ...localConfig,
@@ -1228,9 +1307,6 @@ async function adoptDispatchMatrixRecommendation(repoRoot, userConfigDir, option
1228
1307
  };
1229
1308
  }
1230
1309
  const sharedConfig = await dependencies.readOatConfig(repoRoot);
1231
- if (hasExistingDispatchMatrix(sharedConfig)) {
1232
- await confirmDispatchMatrixOverwrite(context, dependencies, source, options.yes);
1233
- }
1234
1310
  await validateRecommendationCells(repoRoot, recommendation, dependencies, context.logger.warn);
1235
1311
  await dependencies.writeOatConfig(repoRoot, {
1236
1312
  ...sharedConfig,
@@ -1509,7 +1585,7 @@ export function createConfigCommand(overrides = {}) {
1509
1585
  .option('--shared', 'Write to the shared repo config (.oat/config.json)')
1510
1586
  .option('--local', 'Write to the repo-local config (.oat/config.local.json)')
1511
1587
  .option('--user', 'Write to the user-level config (~/.oat/config.json)')
1512
- .option('--yes', 'Replace an existing adopted matrix without prompting')
1588
+ .option('--yes', 'Compatibility flag; adoption always preserves explicit existing cells')
1513
1589
  .action(async (template, options, command) => {
1514
1590
  const context = dependencies.buildCommandContext(readGlobalOptions(command));
1515
1591
  try {
@@ -1528,7 +1604,7 @@ export function createConfigCommand(overrides = {}) {
1528
1604
  surface = 'local';
1529
1605
  else if (options.user)
1530
1606
  surface = 'user';
1531
- await runAdopt(template, { surface, yes: options.yes === true }, context, dependencies);
1607
+ await runAdopt(template, { surface }, context, dependencies);
1532
1608
  }
1533
1609
  catch (error) {
1534
1610
  const message = error instanceof Error ? error.message : String(error);
@@ -2,7 +2,7 @@ import { type CommandContext, type GlobalOptions } from '../../app/command-conte
2
2
  import { type PjmDoctorOptions } from '../pjm/doctor.js';
3
3
  import { type OatConfig, type OatLocalConfig, type UserConfig } from '../../config/oat-config.js';
4
4
  import { type Manifest } from '../../manifest/index.js';
5
- import { type MatrixCellAvailability, type ValidateMatrixCellOptions } from '../../providers/identity/availability.js';
5
+ import { type MatrixCellAvailabilityResponse, type ValidateMatrixCellOptions } from '../../providers/identity/availability.js';
6
6
  import type { ConcreteScope } from '../../shared/types.js';
7
7
  import { type DoctorCheck } from '../../ui/output.js';
8
8
  import { Command } from 'commander';
@@ -22,7 +22,7 @@ interface DoctorDependencies {
22
22
  readOatConfig: (repoRoot: string) => Promise<OatConfig>;
23
23
  readOatLocalConfig: (repoRoot: string) => Promise<OatLocalConfig>;
24
24
  readUserConfig: (userConfigDir: string) => Promise<UserConfig>;
25
- validateMatrixCell: (provider: string, value: string, options: ValidateMatrixCellOptions) => Promise<MatrixCellAvailability>;
25
+ validateMatrixCell: (provider: string, value: string, options: ValidateMatrixCellOptions) => Promise<MatrixCellAvailabilityResponse>;
26
26
  processEnv: NodeJS.ProcessEnv;
27
27
  runPjmDoctorChecks: (repoRoot: string, options?: PjmDoctorOptions) => Promise<DoctorCheck[]>;
28
28
  checkSkillVersions: (scopeRoot: string, assetsRoot: string, pathExists: (path: string) => Promise<boolean>) => Promise<SkillVersionReport>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"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"}