taskplane 0.28.4 → 0.28.6

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 (71) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +215 -215
  3. package/bin/gitignore-patterns.mjs +79 -79
  4. package/bin/rpc-wrapper.mjs +1086 -1086
  5. package/bin/taskplane.mjs +3254 -3254
  6. package/dashboard/public/app.js +2573 -2573
  7. package/dashboard/public/index.html +139 -139
  8. package/dashboard/public/style.css +1882 -1882
  9. package/dashboard/public/taskplane-word-color.svg +18 -18
  10. package/dashboard/public/taskplane-word-white.svg +18 -18
  11. package/dashboard/server.cjs +1666 -1666
  12. package/extensions/reviewer-extension.ts +119 -119
  13. package/extensions/task-orchestrator.ts +28 -28
  14. package/extensions/taskplane/abort.ts +502 -502
  15. package/extensions/taskplane/agent-bridge-extension.ts +838 -765
  16. package/extensions/taskplane/agent-host.ts +833 -745
  17. package/extensions/taskplane/cleanup.ts +747 -747
  18. package/extensions/taskplane/config-loader.ts +1328 -1322
  19. package/extensions/taskplane/config-schema.ts +692 -682
  20. package/extensions/taskplane/config.ts +73 -73
  21. package/extensions/taskplane/context-window.ts +66 -66
  22. package/extensions/taskplane/diagnostic-reports.ts +463 -463
  23. package/extensions/taskplane/diagnostics.ts +385 -385
  24. package/extensions/taskplane/engine-worker-entry.mjs +34 -34
  25. package/extensions/taskplane/engine-worker.ts +381 -381
  26. package/extensions/taskplane/engine.ts +4539 -4527
  27. package/extensions/taskplane/execution.ts +2733 -2708
  28. package/extensions/taskplane/extension.ts +30 -9
  29. package/extensions/taskplane/formatting.ts +773 -773
  30. package/extensions/taskplane/git.ts +90 -90
  31. package/extensions/taskplane/index.ts +28 -28
  32. package/extensions/taskplane/lane-runner.ts +1383 -1360
  33. package/extensions/taskplane/mailbox.ts +689 -689
  34. package/extensions/taskplane/merge.ts +3135 -3135
  35. package/extensions/taskplane/messages.ts +985 -985
  36. package/extensions/taskplane/migrations.ts +278 -278
  37. package/extensions/taskplane/naming.ts +117 -117
  38. package/extensions/taskplane/path-resolver.ts +237 -237
  39. package/extensions/taskplane/persistence.ts +2087 -2087
  40. package/extensions/taskplane/process-registry.ts +416 -416
  41. package/extensions/taskplane/quality-gate.ts +1033 -1033
  42. package/extensions/taskplane/resume.ts +2879 -2878
  43. package/extensions/taskplane/sessions.ts +57 -57
  44. package/extensions/taskplane/settings-loader.ts +136 -136
  45. package/extensions/taskplane/settings-tui.ts +1867 -1867
  46. package/extensions/taskplane/sidecar-telemetry.ts +252 -252
  47. package/extensions/taskplane/supervisor-primer.md +1694 -1694
  48. package/extensions/taskplane/supervisor.ts +4341 -4341
  49. package/extensions/taskplane/task-executor-core.ts +550 -550
  50. package/extensions/taskplane/tmux-compat.ts +37 -37
  51. package/extensions/taskplane/types.ts +4297 -4278
  52. package/extensions/taskplane/verification.ts +542 -542
  53. package/extensions/taskplane/waves.ts +1548 -1548
  54. package/extensions/taskplane/workspace.ts +705 -705
  55. package/extensions/taskplane/worktree.ts +2604 -2505
  56. package/package.json +57 -57
  57. package/skills/create-taskplane-task/SKILL.md +465 -465
  58. package/skills/create-taskplane-task/references/prompt-template.md +285 -285
  59. package/templates/agents/local/supervisor.md +33 -33
  60. package/templates/agents/local/task-merger.md +27 -27
  61. package/templates/agents/local/task-reviewer.md +30 -30
  62. package/templates/agents/local/task-worker.md +34 -34
  63. package/templates/agents/supervisor-routing.md +92 -92
  64. package/templates/agents/supervisor.md +168 -168
  65. package/templates/agents/task-merger.md +214 -214
  66. package/templates/agents/task-reviewer.md +192 -192
  67. package/templates/agents/task-worker.md +505 -429
  68. package/templates/tasks/EXAMPLE-001-hello-world/PROMPT.md +98 -98
  69. package/templates/tasks/EXAMPLE-001-hello-world/STATUS.md +73 -73
  70. package/templates/tasks/EXAMPLE-002-parallel-smoke/PROMPT.md +97 -97
  71. package/templates/tasks/EXAMPLE-002-parallel-smoke/STATUS.md +73 -73
@@ -1,682 +1,692 @@
1
- /**
2
- * Unified project configuration schema for taskplane-config.json
3
- *
4
- * Merges all settings from task-runner.yaml and task-orchestrator.yaml
5
- * into a single JSON-first configuration file with clear sections.
6
- *
7
- * Key naming policy:
8
- * - JSON uses camelCase (e.g., `maxLanes`, `workerContextWindow`)
9
- * - YAML fallback loader maps snake_case keys to camelCase equivalents
10
- * - The runtime config object always uses the interfaces defined here
11
- *
12
- * Section map (old YAML → new JSON):
13
- * task-runner.yaml:
14
- * project → taskRunner.project
15
- * paths → taskRunner.paths
16
- * testing → taskRunner.testing
17
- * standards → taskRunner.standards
18
- * standards_overrides → taskRunner.standardsOverrides
19
- * worker → taskRunner.worker
20
- * reviewer → taskRunner.reviewer
21
- * context → taskRunner.context
22
- * task_areas → taskRunner.taskAreas
23
- * reference_docs → taskRunner.referenceDocs
24
- * never_load → taskRunner.neverLoad
25
- * self_doc_targets → taskRunner.selfDocTargets
26
- * protected_docs → taskRunner.protectedDocs
27
- *
28
- * task-orchestrator.yaml:
29
- * orchestrator → orchestrator.orchestrator
30
- * dependencies → orchestrator.dependencies
31
- * assignment → orchestrator.assignment
32
- * pre_warm → orchestrator.preWarm
33
- * merge → orchestrator.merge
34
- * failure → orchestrator.failure
35
- * monitoring → orchestrator.monitoring
36
- *
37
- * @module config/schema
38
- */
39
-
40
- // ── Config Version ───────────────────────────────────────────────────
41
-
42
- /**
43
- * Current config schema version.
44
- *
45
- * Semantics:
46
- * - Required field in taskplane-config.json (must be present and valid)
47
- * - Initial version: 1
48
- * - Loader behavior for unknown future versions: reject with a clear
49
- * error message telling the user to upgrade Taskplane
50
- * - YAML fallback files have no version field; the loader treats them
51
- * as implicitly version 1
52
- */
53
- export const CONFIG_VERSION = 1;
54
-
55
- // ── Canonical Config Path ────────────────────────────────────────────
56
-
57
- /**
58
- * Canonical filename for the unified JSON config.
59
- * Resolved relative to project root: `.pi/taskplane-config.json`
60
- */
61
- export const PROJECT_CONFIG_FILENAME = "taskplane-config.json";
62
-
63
-
64
- // ── Task Runner Section Interfaces ───────────────────────────────────
65
-
66
- /** Project metadata */
67
- export interface ProjectMetadataConfig {
68
- /** Project display name used in prompts/status UI context */
69
- name: string;
70
- /** Short project description for agent context */
71
- description: string;
72
- }
73
-
74
- /** Path metadata for the project */
75
- export interface PathsConfig {
76
- /** Logical tasks root path metadata */
77
- tasks: string;
78
- /** Path to architecture document used in context references */
79
- architecture?: string;
80
- }
81
-
82
- /** Verification commands available to agents/reviewers */
83
- export interface TestingConfig {
84
- /** Named commands (e.g., { test: "npm test", build: "npm run build" }) */
85
- commands: Record<string, string>;
86
- }
87
-
88
- /** Coding standards for agent context */
89
- export interface StandardsConfig {
90
- /** Docs to treat as coding/review standards references */
91
- docs: string[];
92
- /** Plain-language rules injected into agent context */
93
- rules: string[];
94
- }
95
-
96
- /** Per-area standards override */
97
- export interface StandardsOverride {
98
- /** Override docs for this area */
99
- docs?: string[];
100
- /** Override rules for this area */
101
- rules?: string[];
102
- }
103
-
104
- /** Worker agent configuration */
105
- export interface WorkerConfig {
106
- /** Worker model. Empty string = inherit from active pi session model */
107
- model: string;
108
- /** Tool allowlist passed to worker agent invocations */
109
- tools: string;
110
- /** Thinking mode setting passed to worker agent */
111
- thinking: string;
112
- /** Optional spawn mode override for task-runner (Runtime V2 subprocess-only). */
113
- spawnMode?: "subprocess";
114
- /** Package specifiers to exclude from extension forwarding for worker agents (exact match). @since TP-180 */
115
- excludeExtensions?: string[];
116
- }
117
-
118
- /** Reviewer agent configuration */
119
- export interface ReviewerConfig {
120
- /** Reviewer model (empty = inherit session model) */
121
- model: string;
122
- /** Tool allowlist for reviewer agent */
123
- tools: string;
124
- /** Thinking mode for reviewer */
125
- thinking: string;
126
- /** Package specifiers to exclude from extension forwarding for reviewer agents (exact match). @since TP-180 */
127
- excludeExtensions?: string[];
128
- }
129
-
130
- /** Context/resource limits for task execution */
131
- export interface ContextConfig {
132
- /** Context window size used for worker context pressure tracking.
133
- * Set to 0 (default) for auto-detection from the pi model registry.
134
- * When 0, the task-runner resolves at runtime: ctx.model.contextWindow → 200K fallback. */
135
- workerContextWindow: number;
136
- /** Warn threshold for context utilization (percent) */
137
- warnPercent: number;
138
- /** Hard-stop threshold for context utilization (percent) */
139
- killPercent: number;
140
- /** Max worker iterations per step before failure */
141
- maxWorkerIterations: number;
142
- /** Max revise loops per review stage */
143
- maxReviewCycles: number;
144
- /** Max no-progress iterations before marking failure */
145
- noProgressLimit: number;
146
- /** Optional per-worker wall-clock cap (minutes, used in orchestrated flows) */
147
- maxWorkerMinutes?: number;
148
- }
149
-
150
- /** Task area definition */
151
- export interface TaskAreaConfig {
152
- /** Directory containing task folders */
153
- path: string;
154
- /** Task ID prefix convention for that area */
155
- prefix: string;
156
- /** Area context file path (CONTEXT.md) */
157
- context: string;
158
- /** Optional repo ID for routing tasks in this area (workspace mode only) */
159
- repoId?: string;
160
- }
161
-
162
- /** Self-documentation target definition */
163
- export interface SelfDocTarget {
164
- /** File path where agents should log discoveries */
165
- [key: string]: string;
166
- }
167
-
168
- /**
169
- * Severity threshold for quality gate pass decisions.
170
- *
171
- * - `no_critical`: PASS if no critical findings (important/suggestion allowed)
172
- * - `no_important`: PASS if no critical and fewer than 3 important findings
173
- * - `all_clear`: PASS only if zero findings of any severity
174
- */
175
- export type PassThreshold = "no_critical" | "no_important" | "all_clear";
176
-
177
- /**
178
- * Model fallback behavior when a configured agent model becomes unavailable mid-batch.
179
- *
180
- * - `"inherit"`: Fall back to the session model and retry (default). The task is
181
- * retried without an explicit --model flag, so pi uses whatever model the
182
- * session is configured with.
183
- * - `"fail"`: Fail immediately — the normal failure/retry path handles the error
184
- * without any model substitution.
185
- *
186
- * @since TP-055
187
- */
188
- export type ModelFallbackMode = "inherit" | "fail";
189
-
190
- /** Quality gate configuration — opt-in post-completion review */
191
- export interface QualityGateConfig {
192
- /** Enable quality gate review before .DONE creation (default: false) */
193
- enabled: boolean;
194
- /** Model used for quality gate review agent (empty = inherit session model) */
195
- reviewModel: string;
196
- /** Max total review cycles before marking task failed (default: 2) */
197
- maxReviewCycles: number;
198
- /** Max fix agent cycles per quality gate run (default: 1) */
199
- maxFixCycles: number;
200
- /** Severity threshold for PASS decision (default: "no_critical") */
201
- passThreshold: PassThreshold;
202
- }
203
-
204
-
205
- // ── Task Runner Combined Section ─────────────────────────────────────
206
-
207
- /**
208
- * All task-runner settings, previously from `.pi/task-runner.yaml`.
209
- *
210
- * Contains sections consumed by both the task-runner extension directly
211
- * and by broader ecosystem tooling (skills, workflows, orchestrator).
212
- */
213
- export interface TaskRunnerSection {
214
- /** Project metadata */
215
- project: ProjectMetadataConfig;
216
- /** Path metadata */
217
- paths: PathsConfig;
218
- /** Verification commands */
219
- testing: TestingConfig;
220
- /** Coding standards */
221
- standards: StandardsConfig;
222
- /** Per-area standards overrides, keyed by area name */
223
- standardsOverrides: Record<string, StandardsOverride>;
224
- /** Worker agent configuration */
225
- worker: WorkerConfig;
226
- /** Reviewer agent configuration */
227
- reviewer: ReviewerConfig;
228
- /** Context/resource limits */
229
- context: ContextConfig;
230
- /** Task area definitions, keyed by area name */
231
- taskAreas: Record<string, TaskAreaConfig>;
232
- /** Named reference docs catalog */
233
- referenceDocs: Record<string, string>;
234
- /** Files/docs that should not be loaded into task execution context */
235
- neverLoad: string[];
236
- /** Target anchors where agents should log discoveries */
237
- selfDocTargets: Record<string, string>;
238
- /** Paths requiring explicit user approval before modification */
239
- protectedDocs: string[];
240
- /** Quality gate configuration — opt-in post-completion review */
241
- qualityGate: QualityGateConfig;
242
- /**
243
- * Model fallback behavior when a configured model becomes unavailable mid-batch.
244
- *
245
- * - `"inherit"` (default): Retry the task without an explicit model flag,
246
- * falling back to the session model.
247
- * - `"fail"`: Fail immediately without model substitution.
248
- *
249
- * @since TP-055
250
- */
251
- modelFallback: ModelFallbackMode;
252
- }
253
-
254
-
255
- // ── Orchestrator Section Interfaces ──────────────────────────────────
256
-
257
- /** Core orchestrator settings */
258
- export interface OrchestratorCoreConfig {
259
- /** Maximum parallel execution lanes/worktrees */
260
- maxLanes: number;
261
- /** Where lane worktree directories are created */
262
- worktreeLocation: "sibling" | "subdirectory";
263
- /** Prefix used for worktree directory names and lane branch naming */
264
- worktreePrefix: string;
265
- /** Batch ID format used in logs/branch naming */
266
- batchIdFormat: "timestamp" | "sequential";
267
- /** How lane sessions are spawned (Runtime V2 subprocess-only). */
268
- spawnMode: "subprocess";
269
- /** Prefix for orchestrator session naming */
270
- sessionPrefix: string;
271
- /** Operator identifier. Auto-detected from OS username if empty */
272
- operatorId: string;
273
- /** How completed batches are integrated. manual = user runs /orch-integrate. supervised = supervisor proposes plan, asks confirmation. auto = supervisor executes without asking. */
274
- integration: "manual" | "supervised" | "auto";
275
- }
276
-
277
- /** Dependency resolution settings */
278
- export interface DependenciesConfig {
279
- /** Dependency extraction source */
280
- source: "prompt" | "agent";
281
- /** Cache dependency analysis results between runs */
282
- cache: boolean;
283
- }
284
-
285
- /** Lane assignment settings */
286
- export interface AssignmentConfig {
287
- /** Lane assignment strategy */
288
- strategy: "affinity-first" | "round-robin" | "load-balanced";
289
- /** Relative weights used by size-aware assignment logic */
290
- sizeWeights: Record<string, number>;
291
- }
292
-
293
- /** Pre-warm settings */
294
- export interface PreWarmConfig {
295
- /** Enable automatic pre-warm command detection */
296
- autoDetect: boolean;
297
- /** Named pre-warm commands */
298
- commands: Record<string, string>;
299
- /** Commands always run before wave execution */
300
- always: string[];
301
- }
302
-
303
- /** Merge settings */
304
- export interface MergeConfig {
305
- /** Merge-agent model (empty = inherit active session model) */
306
- model: string;
307
- /** Merge-agent tool allowlist */
308
- tools: string;
309
- /** Merge-agent thinking mode (empty = inherit session thinking) */
310
- thinking: string;
311
- /** Verification commands run after merge operations */
312
- verify: string[];
313
- /** Lane merge ordering policy */
314
- order: "fewest-files-first" | "sequential";
315
- /** Merge-agent timeout in minutes */
316
- timeoutMinutes?: number;
317
- /** Package specifiers to exclude from extension forwarding for merge agents (exact match). @since TP-180 */
318
- excludeExtensions?: string[];
319
- }
320
-
321
- /** Failure policy settings */
322
- export interface FailureConfig {
323
- /** Batch behavior when a task fails */
324
- onTaskFailure: "skip-dependents" | "stop-wave" | "stop-all";
325
- /** Behavior when a merge step fails */
326
- onMergeFailure: "pause" | "abort";
327
- /** Stall detection threshold (minutes) */
328
- stallTimeout: number;
329
- /** Max worker runtime budget per task in orchestrated mode (minutes) */
330
- maxWorkerMinutes: number;
331
- /** Graceful abort wait time (seconds) before forced termination */
332
- abortGracePeriod: number;
333
- }
334
-
335
- /** Monitoring settings */
336
- export interface MonitoringConfig {
337
- /** Poll interval (seconds) for lane/task monitoring loop */
338
- pollInterval: number;
339
- }
340
-
341
- /**
342
- * Verification baseline fingerprinting settings.
343
- *
344
- * Controls orchestrator-side baseline capture and post-merge comparison.
345
- * When enabled, test commands from `taskRunner.testing.commands` are run
346
- * before and after each lane merge to detect genuinely new failures.
347
- *
348
- * This is separate from `merge.verify` (agent-side verification) which
349
- * handles revert-on-failure logic within the merge agent.
350
- */
351
- export interface VerificationConfig {
352
- /**
353
- * Enable verification baseline fingerprinting.
354
- *
355
- * When false (default), no baseline capture or comparison is performed,
356
- * regardless of whether `taskRunner.testing.commands` are configured.
357
- *
358
- * When true, requires `taskRunner.testing.commands` to have at least
359
- * one command configured. If enabled but no commands are configured:
360
- * - strict mode: treats as baseline-unavailable (triggers merge failure)
361
- * - permissive mode: logs a warning and continues without verification
362
- */
363
- enabled: boolean;
364
- /**
365
- * Verification mode controlling behavior when baseline is unavailable.
366
- *
367
- * - "strict": Baseline capture failure or missing commands triggers a
368
- * merge failure. The `failure.onMergeFailure` policy then determines
369
- * whether the batch pauses or aborts.
370
- * - "permissive": Baseline capture failure or missing commands logs a
371
- * warning and continues without orchestrator-side verification.
372
- * Merge-agent verification (`merge.verify`) still applies independently.
373
- *
374
- * Default: "permissive"
375
- */
376
- mode: "strict" | "permissive";
377
- /**
378
- * Number of flaky re-runs when new failures are detected.
379
- *
380
- * When new failures are found after a lane merge, only the commands that
381
- * produced failures are re-run this many times. If failures disappear on
382
- * any re-run, the lane is classified as "flaky_suspected" (warning only).
383
- *
384
- * Set to 0 to disable flaky re-runs (any new failure immediately blocks).
385
- * Default: 1
386
- */
387
- flakyReruns: number;
388
- }
389
-
390
-
391
- // ── Orchestrator Combined Section ────────────────────────────────────
392
-
393
- /**
394
- * All orchestrator settings, previously from `.pi/task-orchestrator.yaml`.
395
- */
396
- /** Supervisor agent settings (TP-041). */
397
- export interface SupervisorSectionConfig {
398
- /** Supervisor model (empty = inherit active session model) */
399
- model: string;
400
- /** Autonomy level for recovery actions */
401
- autonomy: "interactive" | "supervised" | "autonomous";
402
- }
403
-
404
- export interface OrchestratorSection {
405
- /** Core orchestrator settings */
406
- orchestrator: OrchestratorCoreConfig;
407
- /** Dependency resolution */
408
- dependencies: DependenciesConfig;
409
- /** Lane assignment */
410
- assignment: AssignmentConfig;
411
- /** Pre-warm */
412
- preWarm: PreWarmConfig;
413
- /** Merge */
414
- merge: MergeConfig;
415
- /** Failure policy */
416
- failure: FailureConfig;
417
- /** Monitoring */
418
- monitoring: MonitoringConfig;
419
- /** Verification baseline fingerprinting (TP-032) */
420
- verification: VerificationConfig;
421
- /** Supervisor agent (TP-041) */
422
- supervisor: SupervisorSectionConfig;
423
- }
424
-
425
-
426
- // ── Workspace Section Interfaces ─────────────────────────────────────
427
-
428
- /** Workspace repo definition (JSON config shape). */
429
- export interface WorkspaceRepoSectionConfig {
430
- /** Repo root path (relative to workspace root or absolute). */
431
- path: string;
432
- /** Optional default branch override. */
433
- defaultBranch?: string;
434
- }
435
-
436
- /** Workspace routing definition (JSON config shape). */
437
- export interface WorkspaceRoutingSectionConfig {
438
- /** Shared task packet root directory. */
439
- tasksRoot: string;
440
- /** Default repo for unqualified operations. */
441
- defaultRepo: string;
442
- /** Packet-home repo owning PROMPT/STATUS/.DONE. */
443
- taskPacketRepo: string;
444
- /** Strict repo routing mode. */
445
- strict?: boolean;
446
- }
447
-
448
- /** Optional workspace section in taskplane-config.json. */
449
- export interface WorkspaceSectionConfig {
450
- /** Repo map keyed by repo ID. */
451
- repos: Record<string, WorkspaceRepoSectionConfig>;
452
- /** Routing contract for workspace mode. */
453
- routing: WorkspaceRoutingSectionConfig;
454
- }
455
-
456
-
457
- // ── Unified Config ───────────────────────────────────────────────────
458
-
459
- /**
460
- * Unified project configuration — the single source of truth.
461
- *
462
- * This is the runtime config object produced by `loadProjectConfig()`.
463
- * It merges all settings from both YAML files (or the single JSON file)
464
- * into one typed structure.
465
- *
466
- * File: `.pi/taskplane-config.json`
467
- *
468
- * Example JSON structure:
469
- * ```json
470
- * {
471
- * "configVersion": 1,
472
- * "taskRunner": { ... },
473
- * "orchestrator": { ... }
474
- * }
475
- * ```
476
- */
477
- export interface TaskplaneConfig {
478
- /** Schema version — must equal CONFIG_VERSION */
479
- configVersion: number;
480
- /** Task runner settings */
481
- taskRunner: TaskRunnerSection;
482
- /** Orchestrator settings */
483
- orchestrator: OrchestratorSection;
484
- /** Optional workspace config (JSON-first; legacy YAML fallback supported). */
485
- workspace?: WorkspaceSectionConfig;
486
- }
487
-
488
-
489
- // ── Global Preferences (Layer 2) ─────────────────────────────────────
490
-
491
- /**
492
- * Global preferences — personal settings stored per-user.
493
- *
494
- * File: `~/.pi/agent/taskplane/preferences.json`
495
- * (or `$PI_CODING_AGENT_DIR/taskplane/preferences.json` if set)
496
- *
497
- * These are "Layer 2" fields — they override project config (Layer 1)
498
- * for user-scoped settings only. The merge is allowlist-based: only
499
- * the fields defined here can be overridden by global preferences.
500
- * Unknown keys in the preferences file are silently ignored.
501
- *
502
- * Preferences JSON uses camelCase keys matching the runtime config shape.
503
- *
504
- * Layer 2 allowlist:
505
- * - Config-shaped nested overrides (`taskRunner`, `orchestrator`, `workspace`)
506
- * - Legacy flat aliases (`workerModel`, `reviewerModel`, etc.) for backward compatibility
507
- * - Preferences-only keys (`dashboardPort`, `initAgentDefaults`)
508
- */
509
- export interface InitAgentDefaultsPreferences {
510
- /** Worker model default for `taskplane init` prompts (empty = inherit) */
511
- workerModel?: string;
512
- /** Reviewer model default for `taskplane init` prompts (empty = inherit) */
513
- reviewerModel?: string;
514
- /** Merger model default for `taskplane init` prompts (empty = inherit) */
515
- mergeModel?: string;
516
- /** Worker thinking default for `taskplane init` prompts (`""`/`on`/`off`) */
517
- workerThinking?: string;
518
- /** Reviewer thinking default for `taskplane init` prompts (`""`/`on`/`off`) */
519
- reviewerThinking?: string;
520
- /** Merger thinking default for `taskplane init` prompts (`""`/`on`/`off`) */
521
- mergeThinking?: string;
522
- }
523
-
524
- export type DeepPartial<T> = T extends Array<infer U>
525
- ? Array<DeepPartial<U>>
526
- : T extends object
527
- ? { [K in keyof T]?: DeepPartial<T[K]> }
528
- : T;
529
-
530
- export interface GlobalPreferences {
531
- /**
532
- * Global baseline overrides using the same shape as project config.
533
- * All fields are optional and merged deeply into schema defaults.
534
- */
535
- taskRunner?: DeepPartial<TaskRunnerSection>;
536
- orchestrator?: DeepPartial<OrchestratorSection>;
537
- workspace?: DeepPartial<WorkspaceSectionConfig>;
538
-
539
- /** Legacy flat aliases (backward compatibility for existing preferences.json files). */
540
- operatorId?: string;
541
- sessionPrefix?: string;
542
- spawnMode?: "subprocess";
543
- workerModel?: string;
544
- reviewerModel?: string;
545
- mergeModel?: string;
546
- mergeThinking?: string;
547
- supervisorModel?: string;
548
-
549
- /** Preferences-only values (stored globally, not merged into runtime config). */
550
- dashboardPort?: number;
551
- /** Saved defaults used to pre-populate `taskplane init` model/thinking prompts */
552
- initAgentDefaults?: InitAgentDefaultsPreferences;
553
- }
554
-
555
- /** Default (empty) global preferences — all fields undefined means "no override". */
556
- export const DEFAULT_GLOBAL_PREFERENCES: GlobalPreferences = {};
557
-
558
- /**
559
- * Seed values used when first bootstrapping preferences.json.
560
- *
561
- * Kept separate from DEFAULT_GLOBAL_PREFERENCES so runtime fallback semantics
562
- * remain "no override", while first-install scaffolding can provide
563
- * user-friendly init defaults.
564
- */
565
- export const DEFAULT_BOOTSTRAP_GLOBAL_PREFERENCES: GlobalPreferences = {
566
- initAgentDefaults: {
567
- workerModel: "",
568
- reviewerModel: "",
569
- mergeModel: "",
570
- workerThinking: "high",
571
- reviewerThinking: "high",
572
- mergeThinking: "high",
573
- },
574
- };
575
-
576
- /**
577
- * Canonical filename for global preferences.
578
- * Resolved relative to agent directory: `<agentDir>/taskplane/preferences.json`
579
- */
580
- export const GLOBAL_PREFERENCES_FILENAME = "preferences.json";
581
-
582
- /**
583
- * Subdirectory under the agent dir for taskplane preferences.
584
- */
585
- export const GLOBAL_PREFERENCES_SUBDIR = "taskplane";
586
-
587
-
588
- // ── Defaults ─────────────────────────────────────────────────────────
589
-
590
- /** Default task runner section values */
591
- export const DEFAULT_TASK_RUNNER_SECTION: TaskRunnerSection = {
592
- project: { name: "Project", description: "" },
593
- paths: { tasks: "docs/task-management" },
594
- testing: { commands: {} },
595
- standards: { docs: [], rules: [] },
596
- standardsOverrides: {},
597
- worker: { model: "", tools: "read,write,edit,bash,grep,find,ls", thinking: "", excludeExtensions: [] },
598
- reviewer: { model: "", tools: "read,bash,grep,find,ls", thinking: "on", excludeExtensions: [] },
599
- context: {
600
- workerContextWindow: 0,
601
- warnPercent: 85,
602
- killPercent: 95,
603
- maxWorkerIterations: 20,
604
- maxReviewCycles: 2,
605
- noProgressLimit: 3,
606
- },
607
- taskAreas: {},
608
- referenceDocs: {},
609
- neverLoad: [],
610
- selfDocTargets: {},
611
- protectedDocs: [],
612
- qualityGate: {
613
- enabled: false,
614
- reviewModel: "",
615
- maxReviewCycles: 2,
616
- maxFixCycles: 1,
617
- passThreshold: "no_critical",
618
- },
619
- modelFallback: "inherit",
620
- };
621
-
622
- /** Default orchestrator section values */
623
- export const DEFAULT_ORCHESTRATOR_SECTION: OrchestratorSection = {
624
- orchestrator: {
625
- maxLanes: 3,
626
- worktreeLocation: "subdirectory",
627
- worktreePrefix: "taskplane-wt",
628
- batchIdFormat: "timestamp",
629
- spawnMode: "subprocess",
630
- sessionPrefix: "orch",
631
- operatorId: "",
632
- integration: "manual",
633
- },
634
- dependencies: {
635
- source: "prompt",
636
- cache: true,
637
- },
638
- assignment: {
639
- strategy: "affinity-first",
640
- sizeWeights: { S: 1, M: 2, L: 4 },
641
- },
642
- preWarm: {
643
- autoDetect: false,
644
- commands: {},
645
- always: [],
646
- },
647
- merge: {
648
- model: "",
649
- tools: "read,write,edit,bash,grep,find,ls",
650
- thinking: "off",
651
- verify: [],
652
- order: "fewest-files-first",
653
- timeoutMinutes: 90,
654
- excludeExtensions: [],
655
- },
656
- failure: {
657
- onTaskFailure: "skip-dependents",
658
- onMergeFailure: "pause",
659
- stallTimeout: 60,
660
- maxWorkerMinutes: 120,
661
- abortGracePeriod: 60,
662
- },
663
- monitoring: {
664
- pollInterval: 5,
665
- },
666
- verification: {
667
- enabled: false,
668
- mode: "permissive",
669
- flakyReruns: 1,
670
- },
671
- supervisor: {
672
- model: "",
673
- autonomy: "supervised",
674
- },
675
- };
676
-
677
- /** Default unified config */
678
- export const DEFAULT_PROJECT_CONFIG: TaskplaneConfig = {
679
- configVersion: CONFIG_VERSION,
680
- taskRunner: DEFAULT_TASK_RUNNER_SECTION,
681
- orchestrator: DEFAULT_ORCHESTRATOR_SECTION,
682
- };
1
+ /**
2
+ * Unified project configuration schema for taskplane-config.json
3
+ *
4
+ * Merges all settings from task-runner.yaml and task-orchestrator.yaml
5
+ * into a single JSON-first configuration file with clear sections.
6
+ *
7
+ * Key naming policy:
8
+ * - JSON uses camelCase (e.g., `maxLanes`, `workerContextWindow`)
9
+ * - YAML fallback loader maps snake_case keys to camelCase equivalents
10
+ * - The runtime config object always uses the interfaces defined here
11
+ *
12
+ * Section map (old YAML → new JSON):
13
+ * task-runner.yaml:
14
+ * project → taskRunner.project
15
+ * paths → taskRunner.paths
16
+ * testing → taskRunner.testing
17
+ * standards → taskRunner.standards
18
+ * standards_overrides → taskRunner.standardsOverrides
19
+ * worker → taskRunner.worker
20
+ * reviewer → taskRunner.reviewer
21
+ * context → taskRunner.context
22
+ * task_areas → taskRunner.taskAreas
23
+ * reference_docs → taskRunner.referenceDocs
24
+ * never_load → taskRunner.neverLoad
25
+ * self_doc_targets → taskRunner.selfDocTargets
26
+ * protected_docs → taskRunner.protectedDocs
27
+ *
28
+ * task-orchestrator.yaml:
29
+ * orchestrator → orchestrator.orchestrator
30
+ * dependencies → orchestrator.dependencies
31
+ * assignment → orchestrator.assignment
32
+ * pre_warm → orchestrator.preWarm
33
+ * merge → orchestrator.merge
34
+ * failure → orchestrator.failure
35
+ * monitoring → orchestrator.monitoring
36
+ *
37
+ * @module config/schema
38
+ */
39
+
40
+ // ── Config Version ───────────────────────────────────────────────────
41
+
42
+ /**
43
+ * Current config schema version.
44
+ *
45
+ * Semantics:
46
+ * - Required field in taskplane-config.json (must be present and valid)
47
+ * - Initial version: 1
48
+ * - Loader behavior for unknown future versions: reject with a clear
49
+ * error message telling the user to upgrade Taskplane
50
+ * - YAML fallback files have no version field; the loader treats them
51
+ * as implicitly version 1
52
+ */
53
+ export const CONFIG_VERSION = 1;
54
+
55
+ // ── Canonical Config Path ────────────────────────────────────────────
56
+
57
+ /**
58
+ * Canonical filename for the unified JSON config.
59
+ * Resolved relative to project root: `.pi/taskplane-config.json`
60
+ */
61
+ export const PROJECT_CONFIG_FILENAME = "taskplane-config.json";
62
+
63
+
64
+ // ── Task Runner Section Interfaces ───────────────────────────────────
65
+
66
+ /** Project metadata */
67
+ export interface ProjectMetadataConfig {
68
+ /** Project display name used in prompts/status UI context */
69
+ name: string;
70
+ /** Short project description for agent context */
71
+ description: string;
72
+ }
73
+
74
+ /** Path metadata for the project */
75
+ export interface PathsConfig {
76
+ /** Logical tasks root path metadata */
77
+ tasks: string;
78
+ /** Path to architecture document used in context references */
79
+ architecture?: string;
80
+ }
81
+
82
+ /** Verification commands available to agents/reviewers */
83
+ export interface TestingConfig {
84
+ /** Named commands (e.g., { test: "npm test", build: "npm run build" }) */
85
+ commands: Record<string, string>;
86
+ }
87
+
88
+ /** Coding standards for agent context */
89
+ export interface StandardsConfig {
90
+ /** Docs to treat as coding/review standards references */
91
+ docs: string[];
92
+ /** Plain-language rules injected into agent context */
93
+ rules: string[];
94
+ }
95
+
96
+ /** Per-area standards override */
97
+ export interface StandardsOverride {
98
+ /** Override docs for this area */
99
+ docs?: string[];
100
+ /** Override rules for this area */
101
+ rules?: string[];
102
+ }
103
+
104
+ /** Worker agent configuration */
105
+ export interface WorkerConfig {
106
+ /** Worker model. Empty string = inherit from active pi session model */
107
+ model: string;
108
+ /** Tool allowlist passed to worker agent invocations */
109
+ tools: string;
110
+ /** Thinking mode setting passed to worker agent */
111
+ thinking: string;
112
+ /** Optional spawn mode override for task-runner (Runtime V2 subprocess-only). */
113
+ spawnMode?: "subprocess";
114
+ /** Package specifiers to exclude from extension forwarding for worker agents (exact match). @since TP-180 */
115
+ excludeExtensions?: string[];
116
+ }
117
+
118
+ /** Reviewer agent configuration */
119
+ export interface ReviewerConfig {
120
+ /** Reviewer model (empty = inherit session model) */
121
+ model: string;
122
+ /** Tool allowlist for reviewer agent */
123
+ tools: string;
124
+ /** Thinking mode for reviewer */
125
+ thinking: string;
126
+ /** Package specifiers to exclude from extension forwarding for reviewer agents (exact match). @since TP-180 */
127
+ excludeExtensions?: string[];
128
+ }
129
+
130
+ /** Context/resource limits for task execution */
131
+ export interface ContextConfig {
132
+ /** Context window size used for worker context pressure tracking.
133
+ * Set to 0 (default) for auto-detection from the pi model registry.
134
+ * When 0, the task-runner resolves at runtime: ctx.model.contextWindow → 200K fallback. */
135
+ workerContextWindow: number;
136
+ /** Warn threshold for context utilization (percent) */
137
+ warnPercent: number;
138
+ /** Hard-stop threshold for context utilization (percent) */
139
+ killPercent: number;
140
+ /** Max worker iterations per step before failure */
141
+ maxWorkerIterations: number;
142
+ /** Max revise loops per review stage */
143
+ maxReviewCycles: number;
144
+ /** Max no-progress iterations before marking failure */
145
+ noProgressLimit: number;
146
+ /** Optional per-worker wall-clock cap (minutes, used in orchestrated flows) */
147
+ maxWorkerMinutes?: number;
148
+ }
149
+
150
+ /** Task area definition */
151
+ export interface TaskAreaConfig {
152
+ /** Directory containing task folders */
153
+ path: string;
154
+ /** Task ID prefix convention for that area */
155
+ prefix: string;
156
+ /** Area context file path (CONTEXT.md) */
157
+ context: string;
158
+ /** Optional repo ID for routing tasks in this area (workspace mode only) */
159
+ repoId?: string;
160
+ }
161
+
162
+ /** Self-documentation target definition */
163
+ export interface SelfDocTarget {
164
+ /** File path where agents should log discoveries */
165
+ [key: string]: string;
166
+ }
167
+
168
+ /**
169
+ * Severity threshold for quality gate pass decisions.
170
+ *
171
+ * - `no_critical`: PASS if no critical findings (important/suggestion allowed)
172
+ * - `no_important`: PASS if no critical and fewer than 3 important findings
173
+ * - `all_clear`: PASS only if zero findings of any severity
174
+ */
175
+ export type PassThreshold = "no_critical" | "no_important" | "all_clear";
176
+
177
+ /**
178
+ * Model fallback behavior when a configured agent model becomes unavailable mid-batch.
179
+ *
180
+ * - `"inherit"`: Fall back to the session model and retry (default). The task is
181
+ * retried without an explicit --model flag, so pi uses whatever model the
182
+ * session is configured with.
183
+ * - `"fail"`: Fail immediately — the normal failure/retry path handles the error
184
+ * without any model substitution.
185
+ *
186
+ * @since TP-055
187
+ */
188
+ export type ModelFallbackMode = "inherit" | "fail";
189
+
190
+ /** Quality gate configuration — opt-in post-completion review */
191
+ export interface QualityGateConfig {
192
+ /** Enable quality gate review before .DONE creation (default: false) */
193
+ enabled: boolean;
194
+ /** Model used for quality gate review agent (empty = inherit session model) */
195
+ reviewModel: string;
196
+ /** Max total review cycles before marking task failed (default: 2) */
197
+ maxReviewCycles: number;
198
+ /** Max fix agent cycles per quality gate run (default: 1) */
199
+ maxFixCycles: number;
200
+ /** Severity threshold for PASS decision (default: "no_critical") */
201
+ passThreshold: PassThreshold;
202
+ }
203
+
204
+
205
+ // ── Task Runner Combined Section ─────────────────────────────────────
206
+
207
+ /**
208
+ * All task-runner settings, previously from `.pi/task-runner.yaml`.
209
+ *
210
+ * Contains sections consumed by both the task-runner extension directly
211
+ * and by broader ecosystem tooling (skills, workflows, orchestrator).
212
+ */
213
+ export interface TaskRunnerSection {
214
+ /** Project metadata */
215
+ project: ProjectMetadataConfig;
216
+ /** Path metadata */
217
+ paths: PathsConfig;
218
+ /** Verification commands */
219
+ testing: TestingConfig;
220
+ /** Coding standards */
221
+ standards: StandardsConfig;
222
+ /** Per-area standards overrides, keyed by area name */
223
+ standardsOverrides: Record<string, StandardsOverride>;
224
+ /** Worker agent configuration */
225
+ worker: WorkerConfig;
226
+ /** Reviewer agent configuration */
227
+ reviewer: ReviewerConfig;
228
+ /** Context/resource limits */
229
+ context: ContextConfig;
230
+ /** Task area definitions, keyed by area name */
231
+ taskAreas: Record<string, TaskAreaConfig>;
232
+ /** Named reference docs catalog */
233
+ referenceDocs: Record<string, string>;
234
+ /** Files/docs that should not be loaded into task execution context */
235
+ neverLoad: string[];
236
+ /** Target anchors where agents should log discoveries */
237
+ selfDocTargets: Record<string, string>;
238
+ /** Paths requiring explicit user approval before modification */
239
+ protectedDocs: string[];
240
+ /** Quality gate configuration — opt-in post-completion review */
241
+ qualityGate: QualityGateConfig;
242
+ /**
243
+ * Model fallback behavior when a configured model becomes unavailable mid-batch.
244
+ *
245
+ * - `"inherit"` (default): Retry the task without an explicit model flag,
246
+ * falling back to the session model.
247
+ * - `"fail"`: Fail immediately without model substitution.
248
+ *
249
+ * @since TP-055
250
+ */
251
+ modelFallback: ModelFallbackMode;
252
+ }
253
+
254
+
255
+ // ── Orchestrator Section Interfaces ──────────────────────────────────
256
+
257
+ /** Core orchestrator settings */
258
+ export interface OrchestratorCoreConfig {
259
+ /** Maximum parallel execution lanes/worktrees */
260
+ maxLanes: number;
261
+ /** Where lane worktree directories are created */
262
+ worktreeLocation: "sibling" | "subdirectory";
263
+ /** Prefix used for worktree directory names and lane branch naming */
264
+ worktreePrefix: string;
265
+ /** Batch ID format used in logs/branch naming */
266
+ batchIdFormat: "timestamp" | "sequential";
267
+ /** How lane sessions are spawned (Runtime V2 subprocess-only). */
268
+ spawnMode: "subprocess";
269
+ /** Prefix for orchestrator session naming */
270
+ sessionPrefix: string;
271
+ /** Operator identifier. Auto-detected from OS username if empty */
272
+ operatorId: string;
273
+ /** How completed batches are integrated. manual = user runs /orch-integrate. supervised = supervisor proposes plan, asks confirmation. auto = supervisor executes without asking. */
274
+ integration: "manual" | "supervised" | "auto";
275
+ }
276
+
277
+ /** Dependency resolution settings */
278
+ export interface DependenciesConfig {
279
+ /** Dependency extraction source */
280
+ source: "prompt" | "agent";
281
+ /** Cache dependency analysis results between runs */
282
+ cache: boolean;
283
+ }
284
+
285
+ /** Lane assignment settings */
286
+ export interface AssignmentConfig {
287
+ /** Lane assignment strategy */
288
+ strategy: "affinity-first" | "round-robin" | "load-balanced";
289
+ /** Relative weights used by size-aware assignment logic */
290
+ sizeWeights: Record<string, number>;
291
+ }
292
+
293
+ /** Pre-warm settings */
294
+ export interface PreWarmConfig {
295
+ /** Enable automatic pre-warm command detection */
296
+ autoDetect: boolean;
297
+ /** Named pre-warm commands */
298
+ commands: Record<string, string>;
299
+ /** Commands always run before wave execution */
300
+ always: string[];
301
+ }
302
+
303
+ /** Merge settings */
304
+ export interface MergeConfig {
305
+ /** Merge-agent model (empty = inherit active session model) */
306
+ model: string;
307
+ /** Merge-agent tool allowlist */
308
+ tools: string;
309
+ /** Merge-agent thinking mode (empty = inherit session thinking) */
310
+ thinking: string;
311
+ /** Verification commands run after merge operations */
312
+ verify: string[];
313
+ /** Lane merge ordering policy */
314
+ order: "fewest-files-first" | "sequential";
315
+ /** Merge-agent timeout in minutes */
316
+ timeoutMinutes?: number;
317
+ /** Package specifiers to exclude from extension forwarding for merge agents (exact match). @since TP-180 */
318
+ excludeExtensions?: string[];
319
+ }
320
+
321
+ /** Failure policy settings */
322
+ export interface FailureConfig {
323
+ /** Batch behavior when a task fails */
324
+ onTaskFailure: "skip-dependents" | "stop-wave" | "stop-all";
325
+ /** Behavior when a merge step fails */
326
+ onMergeFailure: "pause" | "abort";
327
+ /** Stall detection threshold (minutes) */
328
+ stallTimeout: number;
329
+ /** Max worker runtime budget per task in orchestrated mode (minutes) */
330
+ maxWorkerMinutes: number;
331
+ /** Graceful abort wait time (seconds) before forced termination */
332
+ abortGracePeriod: number;
333
+ }
334
+
335
+ /** Monitoring settings */
336
+ export interface MonitoringConfig {
337
+ /** Poll interval (seconds) for lane/task monitoring loop */
338
+ pollInterval: number;
339
+ }
340
+
341
+ /**
342
+ * Verification baseline fingerprinting settings.
343
+ *
344
+ * Controls orchestrator-side baseline capture and post-merge comparison.
345
+ * When enabled, test commands from `taskRunner.testing.commands` are run
346
+ * before and after each lane merge to detect genuinely new failures.
347
+ *
348
+ * This is separate from `merge.verify` (agent-side verification) which
349
+ * handles revert-on-failure logic within the merge agent.
350
+ */
351
+ export interface VerificationConfig {
352
+ /**
353
+ * Enable verification baseline fingerprinting.
354
+ *
355
+ * When false (default), no baseline capture or comparison is performed,
356
+ * regardless of whether `taskRunner.testing.commands` are configured.
357
+ *
358
+ * When true, requires `taskRunner.testing.commands` to have at least
359
+ * one command configured. If enabled but no commands are configured:
360
+ * - strict mode: treats as baseline-unavailable (triggers merge failure)
361
+ * - permissive mode: logs a warning and continues without verification
362
+ */
363
+ enabled: boolean;
364
+ /**
365
+ * Verification mode controlling behavior when baseline is unavailable.
366
+ *
367
+ * - "strict": Baseline capture failure or missing commands triggers a
368
+ * merge failure. The `failure.onMergeFailure` policy then determines
369
+ * whether the batch pauses or aborts.
370
+ * - "permissive": Baseline capture failure or missing commands logs a
371
+ * warning and continues without orchestrator-side verification.
372
+ * Merge-agent verification (`merge.verify`) still applies independently.
373
+ *
374
+ * Default: "permissive"
375
+ */
376
+ mode: "strict" | "permissive";
377
+ /**
378
+ * Number of flaky re-runs when new failures are detected.
379
+ *
380
+ * When new failures are found after a lane merge, only the commands that
381
+ * produced failures are re-run this many times. If failures disappear on
382
+ * any re-run, the lane is classified as "flaky_suspected" (warning only).
383
+ *
384
+ * Set to 0 to disable flaky re-runs (any new failure immediately blocks).
385
+ * Default: 1
386
+ */
387
+ flakyReruns: number;
388
+ }
389
+
390
+
391
+ // ── Orchestrator Combined Section ────────────────────────────────────
392
+
393
+ /**
394
+ * All orchestrator settings, previously from `.pi/task-orchestrator.yaml`.
395
+ */
396
+ /** Supervisor agent settings (TP-041). */
397
+ export interface SupervisorSectionConfig {
398
+ /** Supervisor model (empty = inherit active session model) */
399
+ model: string;
400
+ /** Autonomy level for recovery actions */
401
+ autonomy: "interactive" | "supervised" | "autonomous";
402
+ }
403
+
404
+ export interface OrchestratorSection {
405
+ /** Core orchestrator settings */
406
+ orchestrator: OrchestratorCoreConfig;
407
+ /** Dependency resolution */
408
+ dependencies: DependenciesConfig;
409
+ /** Lane assignment */
410
+ assignment: AssignmentConfig;
411
+ /** Pre-warm */
412
+ preWarm: PreWarmConfig;
413
+ /** Merge */
414
+ merge: MergeConfig;
415
+ /** Failure policy */
416
+ failure: FailureConfig;
417
+ /** Monitoring */
418
+ monitoring: MonitoringConfig;
419
+ /** Verification baseline fingerprinting (TP-032) */
420
+ verification: VerificationConfig;
421
+ /** Supervisor agent (TP-041) */
422
+ supervisor: SupervisorSectionConfig;
423
+ }
424
+
425
+
426
+ // ── Workspace Section Interfaces ─────────────────────────────────────
427
+
428
+ /** Workspace repo definition (JSON config shape). */
429
+ export interface WorkspaceRepoSectionConfig {
430
+ /** Repo root path (relative to workspace root or absolute). */
431
+ path: string;
432
+ /** Optional default branch override. */
433
+ defaultBranch?: string;
434
+ }
435
+
436
+ /** Workspace routing definition (JSON config shape). */
437
+ export interface WorkspaceRoutingSectionConfig {
438
+ /** Shared task packet root directory. */
439
+ tasksRoot: string;
440
+ /** Default repo for unqualified operations. */
441
+ defaultRepo: string;
442
+ /** Packet-home repo owning PROMPT/STATUS/.DONE. */
443
+ taskPacketRepo: string;
444
+ /** Strict repo routing mode. */
445
+ strict?: boolean;
446
+ }
447
+
448
+ /** Optional workspace section in taskplane-config.json. */
449
+ export interface WorkspaceSectionConfig {
450
+ /** Repo map keyed by repo ID. */
451
+ repos: Record<string, WorkspaceRepoSectionConfig>;
452
+ /** Routing contract for workspace mode. */
453
+ routing: WorkspaceRoutingSectionConfig;
454
+ }
455
+
456
+
457
+ // ── Unified Config ───────────────────────────────────────────────────
458
+
459
+ /**
460
+ * Unified project configuration — the single source of truth.
461
+ *
462
+ * This is the runtime config object produced by `loadProjectConfig()`.
463
+ * It merges all settings from both YAML files (or the single JSON file)
464
+ * into one typed structure.
465
+ *
466
+ * File: `.pi/taskplane-config.json`
467
+ *
468
+ * Example JSON structure:
469
+ * ```json
470
+ * {
471
+ * "configVersion": 1,
472
+ * "taskRunner": { ... },
473
+ * "orchestrator": { ... }
474
+ * }
475
+ * ```
476
+ */
477
+ export interface TaskplaneConfig {
478
+ /** Schema version — must equal CONFIG_VERSION */
479
+ configVersion: number;
480
+ /** Task runner settings */
481
+ taskRunner: TaskRunnerSection;
482
+ /** Orchestrator settings */
483
+ orchestrator: OrchestratorSection;
484
+ /** Optional workspace config (JSON-first; legacy YAML fallback supported). */
485
+ workspace?: WorkspaceSectionConfig;
486
+ }
487
+
488
+
489
+ // ── Global Preferences (Layer 2) ─────────────────────────────────────
490
+
491
+ /**
492
+ * Global preferences — personal settings stored per-user.
493
+ *
494
+ * File: `~/.pi/agent/taskplane/preferences.json`
495
+ * (or `$PI_CODING_AGENT_DIR/taskplane/preferences.json` if set)
496
+ *
497
+ * These are "Layer 2" fields — they override project config (Layer 1)
498
+ * for user-scoped settings only. The merge is allowlist-based: only
499
+ * the fields defined here can be overridden by global preferences.
500
+ * Unknown keys in the preferences file are silently ignored.
501
+ *
502
+ * Preferences JSON uses camelCase keys matching the runtime config shape.
503
+ *
504
+ * Layer 2 allowlist:
505
+ * - Config-shaped nested overrides (`taskRunner`, `orchestrator`, `workspace`)
506
+ * - Legacy flat aliases (`workerModel`, `reviewerModel`, etc.) for backward compatibility
507
+ * - Preferences-only keys (`dashboardPort`, `initAgentDefaults`)
508
+ */
509
+ export interface InitAgentDefaultsPreferences {
510
+ /** Worker model default for `taskplane init` prompts (empty = inherit) */
511
+ workerModel?: string;
512
+ /** Reviewer model default for `taskplane init` prompts (empty = inherit) */
513
+ reviewerModel?: string;
514
+ /** Merger model default for `taskplane init` prompts (empty = inherit) */
515
+ mergeModel?: string;
516
+ /** Worker thinking default for `taskplane init` prompts (`""`/`on`/`off`) */
517
+ workerThinking?: string;
518
+ /** Reviewer thinking default for `taskplane init` prompts (`""`/`on`/`off`) */
519
+ reviewerThinking?: string;
520
+ /** Merger thinking default for `taskplane init` prompts (`""`/`on`/`off`) */
521
+ mergeThinking?: string;
522
+ }
523
+
524
+ export type DeepPartial<T> = T extends Array<infer U>
525
+ ? Array<DeepPartial<U>>
526
+ : T extends object
527
+ ? { [K in keyof T]?: DeepPartial<T[K]> }
528
+ : T;
529
+
530
+ export interface GlobalPreferences {
531
+ /**
532
+ * Global baseline overrides using the same shape as project config.
533
+ * All fields are optional and merged deeply into schema defaults.
534
+ */
535
+ taskRunner?: DeepPartial<TaskRunnerSection>;
536
+ orchestrator?: DeepPartial<OrchestratorSection>;
537
+ workspace?: DeepPartial<WorkspaceSectionConfig>;
538
+
539
+ /** Legacy flat aliases (backward compatibility for existing preferences.json files). */
540
+ operatorId?: string;
541
+ sessionPrefix?: string;
542
+ spawnMode?: "subprocess";
543
+ workerModel?: string;
544
+ reviewerModel?: string;
545
+ mergeModel?: string;
546
+ mergeThinking?: string;
547
+ supervisorModel?: string;
548
+
549
+ /** Preferences-only values (stored globally, not merged into runtime config). */
550
+ dashboardPort?: number;
551
+ /** Saved defaults used to pre-populate `taskplane init` model/thinking prompts */
552
+ initAgentDefaults?: InitAgentDefaultsPreferences;
553
+ }
554
+
555
+ /** Default (empty) global preferences — all fields undefined means "no override". */
556
+ export const DEFAULT_GLOBAL_PREFERENCES: GlobalPreferences = {};
557
+
558
+ /**
559
+ * Seed values used when first bootstrapping preferences.json.
560
+ *
561
+ * Kept separate from DEFAULT_GLOBAL_PREFERENCES so runtime fallback semantics
562
+ * remain "no override", while first-install scaffolding can provide
563
+ * user-friendly init defaults.
564
+ */
565
+ export const DEFAULT_BOOTSTRAP_GLOBAL_PREFERENCES: GlobalPreferences = {
566
+ initAgentDefaults: {
567
+ workerModel: "",
568
+ reviewerModel: "",
569
+ mergeModel: "",
570
+ workerThinking: "high",
571
+ reviewerThinking: "high",
572
+ mergeThinking: "high",
573
+ },
574
+ };
575
+
576
+ /**
577
+ * Canonical filename for global preferences.
578
+ * Resolved relative to agent directory: `<agentDir>/taskplane/preferences.json`
579
+ */
580
+ export const GLOBAL_PREFERENCES_FILENAME = "preferences.json";
581
+
582
+ /**
583
+ * Subdirectory under the agent dir for taskplane preferences.
584
+ */
585
+ export const GLOBAL_PREFERENCES_SUBDIR = "taskplane";
586
+
587
+
588
+ // ── Defaults ─────────────────────────────────────────────────────────
589
+
590
+ /** Default task runner section values */
591
+ export const DEFAULT_TASK_RUNNER_SECTION: TaskRunnerSection = {
592
+ project: { name: "Project", description: "" },
593
+ paths: { tasks: "docs/task-management" },
594
+ testing: { commands: {} },
595
+ standards: { docs: [], rules: [] },
596
+ standardsOverrides: {},
597
+ // NOTE (TP-184): The user-tools default literal here mirrors
598
+ // `DEFAULT_WORKER_USER_TOOLS` in `agent-host.ts`. We keep the literal
599
+ // instead of importing the constant because this file is currently
600
+ // import-free (pure schema/defaults) and importing from agent-host.ts
601
+ // would pull child_process/fs into the schema layer. If you change the
602
+ // default, update both copies. Engine bridge tools are appended at the
603
+ // lane-runner spawn site by `buildWorkerToolsAllowlist()`, not here.
604
+ worker: { model: "", tools: "read,write,edit,bash,grep,find,ls", thinking: "", excludeExtensions: [] },
605
+ reviewer: { model: "", tools: "read,bash,grep,find,ls", thinking: "on", excludeExtensions: [] },
606
+ context: {
607
+ workerContextWindow: 0,
608
+ warnPercent: 85,
609
+ killPercent: 95,
610
+ maxWorkerIterations: 20,
611
+ maxReviewCycles: 2,
612
+ noProgressLimit: 3,
613
+ },
614
+ taskAreas: {},
615
+ referenceDocs: {},
616
+ neverLoad: [],
617
+ selfDocTargets: {},
618
+ protectedDocs: [],
619
+ qualityGate: {
620
+ enabled: false,
621
+ reviewModel: "",
622
+ maxReviewCycles: 2,
623
+ maxFixCycles: 1,
624
+ passThreshold: "no_critical",
625
+ },
626
+ modelFallback: "inherit",
627
+ };
628
+
629
+ /** Default orchestrator section values */
630
+ export const DEFAULT_ORCHESTRATOR_SECTION: OrchestratorSection = {
631
+ orchestrator: {
632
+ maxLanes: 3,
633
+ worktreeLocation: "subdirectory",
634
+ worktreePrefix: "taskplane-wt",
635
+ batchIdFormat: "timestamp",
636
+ spawnMode: "subprocess",
637
+ sessionPrefix: "orch",
638
+ operatorId: "",
639
+ integration: "manual",
640
+ },
641
+ dependencies: {
642
+ source: "prompt",
643
+ cache: true,
644
+ },
645
+ assignment: {
646
+ strategy: "affinity-first",
647
+ sizeWeights: { S: 1, M: 2, L: 4 },
648
+ },
649
+ preWarm: {
650
+ autoDetect: false,
651
+ commands: {},
652
+ always: [],
653
+ },
654
+ merge: {
655
+ model: "",
656
+ // NOTE (TP-184): Mirrors `DEFAULT_WORKER_USER_TOOLS`. Merge agent does
657
+ // not run through `buildWorkerToolsAllowlist()` (no bridge-tool needs)
658
+ // so this literal is independent of the worker allowlist plumbing.
659
+ tools: "read,write,edit,bash,grep,find,ls",
660
+ thinking: "off",
661
+ verify: [],
662
+ order: "fewest-files-first",
663
+ timeoutMinutes: 90,
664
+ excludeExtensions: [],
665
+ },
666
+ failure: {
667
+ onTaskFailure: "skip-dependents",
668
+ onMergeFailure: "pause",
669
+ stallTimeout: 60,
670
+ maxWorkerMinutes: 120,
671
+ abortGracePeriod: 60,
672
+ },
673
+ monitoring: {
674
+ pollInterval: 5,
675
+ },
676
+ verification: {
677
+ enabled: false,
678
+ mode: "permissive",
679
+ flakyReruns: 1,
680
+ },
681
+ supervisor: {
682
+ model: "",
683
+ autonomy: "supervised",
684
+ },
685
+ };
686
+
687
+ /** Default unified config */
688
+ export const DEFAULT_PROJECT_CONFIG: TaskplaneConfig = {
689
+ configVersion: CONFIG_VERSION,
690
+ taskRunner: DEFAULT_TASK_RUNNER_SECTION,
691
+ orchestrator: DEFAULT_ORCHESTRATOR_SECTION,
692
+ };