coding-agent-harness 1.0.2 → 1.0.4

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 (177) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/CONTRIBUTING.md +98 -0
  3. package/README.md +211 -86
  4. package/README.zh-CN.md +54 -34
  5. package/SKILL.md +25 -18
  6. package/docs-release/README.md +9 -5
  7. package/docs-release/architecture/overview.md +17 -5
  8. package/docs-release/architecture/overview.zh-CN.md +9 -5
  9. package/docs-release/assets/dashboard-overview.png +0 -0
  10. package/docs-release/guides/agent-installation.en-US.md +31 -8
  11. package/docs-release/guides/agent-installation.md +34 -9
  12. package/docs-release/guides/contributing.md +100 -0
  13. package/docs-release/guides/contributing.zh-CN.md +99 -0
  14. package/docs-release/guides/document-audience-and-surfaces.en-US.md +3 -2
  15. package/docs-release/guides/document-audience-and-surfaces.md +3 -2
  16. package/docs-release/guides/full-legacy-migration-subagent-strategy.md +2 -2
  17. package/docs-release/guides/full-legacy-migration-subagent-strategy.zh-CN.md +2 -2
  18. package/docs-release/guides/legacy-migration-agent-prompt.md +0 -11
  19. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +0 -11
  20. package/docs-release/guides/migration-playbook.en-US.md +14 -15
  21. package/docs-release/guides/migration-playbook.md +14 -15
  22. package/docs-release/guides/parent-control-repository-pattern.en-US.md +7 -5
  23. package/docs-release/guides/parent-control-repository-pattern.md +7 -5
  24. package/docs-release/guides/preset-development.md +214 -0
  25. package/docs-release/guides/repository-operating-models.en-US.md +5 -4
  26. package/docs-release/guides/repository-operating-models.md +5 -4
  27. package/docs-release/guides/task-state-machine.en-US.md +207 -0
  28. package/docs-release/guides/task-state-machine.md +214 -0
  29. package/docs-release/intl/en-US.md +1 -1
  30. package/docs-release/intl/zh-CN.md +1 -1
  31. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/findings.md +7 -0
  32. package/package.json +8 -3
  33. package/presets/legacy-migration/checks/preset-check.mjs +3 -0
  34. package/presets/legacy-migration/preset.yaml +134 -0
  35. package/presets/legacy-migration/scripts/plan-work-queue.mjs +4 -0
  36. package/presets/legacy-migration/scripts/scaffold-task-contracts.mjs +4 -0
  37. package/presets/legacy-migration/templates/execution_strategy.append.md +18 -0
  38. package/presets/legacy-migration/templates/findings.seed.md +17 -0
  39. package/presets/legacy-migration/templates/review.seed.md +12 -0
  40. package/presets/legacy-migration/templates/task_plan.append.md +9 -0
  41. package/presets/legacy-migration/templates/visual_map.append.md +12 -0
  42. package/presets/legacy-migration/workbench/dashboard-panels.yaml +2 -0
  43. package/presets/legacy-migration/workbench/migration-queue.schema.json +23 -0
  44. package/presets/lesson-sedimentation/preset.yaml +23 -0
  45. package/presets/lesson-sedimentation/templates/prompt.md +23 -0
  46. package/presets/module/preset.yaml +25 -0
  47. package/presets/module/templates/execution_strategy.append.md +8 -0
  48. package/presets/module/templates/task_plan.append.md +17 -0
  49. package/presets/standard-task/preset.yaml +31 -0
  50. package/presets/standard-task/templates/task_plan.append.md +7 -0
  51. package/references/adversarial-review-standard.md +2 -2
  52. package/references/agents-md-pattern.md +2 -2
  53. package/references/delivery-operating-model-standard.md +3 -3
  54. package/references/docs-directory-standard.md +6 -7
  55. package/references/harness-ledger.md +53 -96
  56. package/references/lessons-governance.md +88 -93
  57. package/references/module-parallel-standard.md +14 -14
  58. package/references/planning-loop.md +12 -6
  59. package/references/pull-request-standard.md +118 -0
  60. package/references/repo-governance-standard.md +11 -2
  61. package/references/review-routing-standard.md +7 -1
  62. package/references/ssot-governance.md +67 -59
  63. package/references/taskr-gap-analysis.md +600 -0
  64. package/references/walkthrough-closeout.md +7 -7
  65. package/scripts/check-harness.mjs +40 -301
  66. package/scripts/commands/dashboard-command.mjs +67 -0
  67. package/scripts/commands/migration-command.mjs +96 -0
  68. package/scripts/commands/preset-command.mjs +73 -0
  69. package/scripts/commands/task-command.mjs +327 -0
  70. package/scripts/harness.mjs +55 -260
  71. package/scripts/lib/capability-registry.mjs +66 -8
  72. package/scripts/lib/check-module-parallel.mjs +237 -0
  73. package/scripts/lib/check-profiles.mjs +61 -153
  74. package/scripts/lib/check-task-contracts.mjs +47 -0
  75. package/scripts/lib/core-shared.mjs +10 -0
  76. package/scripts/lib/dashboard-data.mjs +29 -6
  77. package/scripts/lib/dashboard-workbench.mjs +52 -12
  78. package/scripts/lib/dashboard-writer.mjs +14 -2
  79. package/scripts/lib/git-status-summary.mjs +46 -0
  80. package/scripts/lib/governance-index-generator.mjs +174 -0
  81. package/scripts/lib/governance-sync.mjs +514 -0
  82. package/scripts/lib/governance-table-boundary.mjs +175 -0
  83. package/scripts/lib/harness-core.mjs +5 -0
  84. package/scripts/lib/lesson-maintenance.mjs +36 -29
  85. package/scripts/lib/migration-support.mjs +1 -1
  86. package/scripts/lib/preset-audit-contracts.mjs +37 -0
  87. package/scripts/lib/preset-engine.mjs +497 -0
  88. package/scripts/lib/preset-registry.mjs +627 -0
  89. package/scripts/lib/preset-resource-contracts.mjs +83 -0
  90. package/scripts/lib/review-confirm-git-gate.mjs +248 -0
  91. package/scripts/lib/status-dashboard-renderer.mjs +102 -0
  92. package/scripts/lib/subagent-authorization-audit.mjs +196 -0
  93. package/scripts/lib/task-completion-consistency.mjs +16 -0
  94. package/scripts/lib/task-index.mjs +93 -0
  95. package/scripts/lib/task-lesson-candidates.mjs +242 -0
  96. package/scripts/lib/task-lesson-sedimentation.mjs +326 -0
  97. package/scripts/lib/task-lifecycle/review-confirm.mjs +101 -0
  98. package/scripts/lib/task-lifecycle/review-gates.mjs +70 -0
  99. package/scripts/lib/task-lifecycle/text-utils.mjs +24 -0
  100. package/scripts/lib/task-lifecycle.mjs +297 -403
  101. package/scripts/lib/task-review-model.mjs +469 -0
  102. package/scripts/lib/task-scanner.mjs +130 -236
  103. package/scripts/lib/task-tombstone-commands.mjs +140 -0
  104. package/scripts/postinstall.mjs +14 -0
  105. package/skills/preset-creator/SKILL.md +179 -0
  106. package/skills/preset-creator/references/complex-task-skeleton/README.md +31 -0
  107. package/skills/preset-creator/references/complex-task-skeleton/artifacts/INDEX.md +12 -0
  108. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -0
  109. package/skills/preset-creator/references/complex-task-skeleton/execution_strategy.md +71 -0
  110. package/skills/preset-creator/references/complex-task-skeleton/findings.md +24 -0
  111. package/skills/preset-creator/references/complex-task-skeleton/lesson_candidates.md +70 -0
  112. package/skills/preset-creator/references/complex-task-skeleton/long-running-task-contract.md +76 -0
  113. package/skills/preset-creator/references/complex-task-skeleton/progress.md +33 -0
  114. package/skills/preset-creator/references/complex-task-skeleton/references/INDEX.md +13 -0
  115. package/skills/preset-creator/references/complex-task-skeleton/review.md +107 -0
  116. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +111 -0
  117. package/skills/preset-creator/references/complex-task-skeleton/visual_map.md +50 -0
  118. package/skills/preset-creator/references/preset-package-skeleton.md +296 -0
  119. package/templates/AGENTS.md.template +19 -15
  120. package/templates/dashboard/assets/app-src/00-state.js +1 -0
  121. package/templates/dashboard/assets/app-src/10-router.js +2 -1
  122. package/templates/dashboard/assets/app-src/20-overview.js +11 -5
  123. package/templates/dashboard/assets/app-src/30-tasks.js +92 -246
  124. package/templates/dashboard/assets/app-src/35-task-detail.js +246 -0
  125. package/templates/dashboard/assets/app-src/45-review.js +241 -22
  126. package/templates/dashboard/assets/app-src/50-migration.js +24 -10
  127. package/templates/dashboard/assets/app-src/90-bindings.js +171 -29
  128. package/templates/dashboard/assets/app.css +698 -156
  129. package/templates/dashboard/assets/app.css.manifest.json +9 -0
  130. package/templates/dashboard/assets/app.js +662 -91
  131. package/templates/dashboard/assets/app.manifest.json +1 -0
  132. package/templates/dashboard/assets/css-src/00-foundation.css +342 -0
  133. package/templates/dashboard/assets/css-src/10-panels-flow.css +236 -0
  134. package/templates/dashboard/assets/css-src/20-briefs-controls.css +398 -0
  135. package/templates/dashboard/assets/css-src/30-task-index.css +739 -0
  136. package/templates/dashboard/assets/css-src/35-review-workspace.css +507 -0
  137. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +427 -0
  138. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +551 -0
  139. package/templates/dashboard/assets/i18n.js +123 -21
  140. package/templates/ledger/Harness-Ledger.md +13 -25
  141. package/templates/lessons/lesson-arch-process-change.md +1 -1
  142. package/templates/lessons/lesson-new-doc.md +1 -1
  143. package/templates/lessons/lesson-ref-change.md +1 -1
  144. package/templates/planning/execution_strategy.md +31 -0
  145. package/templates/planning/lesson_candidates.md +18 -6
  146. package/templates/planning/optional/artifacts/INDEX.md +3 -3
  147. package/templates/planning/optional/references/INDEX.md +3 -3
  148. package/templates/planning/review.md +59 -0
  149. package/templates/planning/task_plan.md +36 -13
  150. package/templates/reference/execution-workflow-standard.md +4 -3
  151. package/templates/reference/pull-request-standard.md +80 -0
  152. package/templates/reference/repo-governance-standard.md +7 -6
  153. package/templates/reference/review-routing-standard.md +6 -0
  154. package/templates/reference/walkthrough-standard.md +2 -1
  155. package/templates/verifier/verifier-output.md +1 -1
  156. package/templates-zh-CN/AGENTS.md.template +20 -16
  157. package/templates-zh-CN/ledger/Harness-Ledger.md +17 -40
  158. package/templates-zh-CN/planning/execution_strategy.md +30 -0
  159. package/templates-zh-CN/planning/lesson_candidates.md +18 -6
  160. package/templates-zh-CN/planning/review.md +59 -1
  161. package/templates-zh-CN/planning/task_plan.md +30 -10
  162. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  163. package/templates-zh-CN/reference/docs-library-standard.md +1 -1
  164. package/templates-zh-CN/reference/execution-workflow-standard.md +4 -3
  165. package/templates-zh-CN/reference/harness-ledger-standard.md +2 -2
  166. package/templates-zh-CN/reference/pull-request-standard.md +106 -0
  167. package/templates-zh-CN/reference/repo-governance-standard.md +4 -3
  168. package/templates-zh-CN/reference/review-routing-standard.md +8 -1
  169. package/templates-zh-CN/reference/walkthrough-standard.md +3 -2
  170. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +1 -1
  171. package/docs-release/assets/dashboard-overview-en.png +0 -0
  172. package/scripts/smoke-dashboard.mjs +0 -92
  173. package/scripts/test-harness.mjs +0 -1395
  174. package/templates/ssot/Feature-SSoT.md +0 -43
  175. package/templates/ssot/Lessons-SSoT.md +0 -44
  176. package/templates-zh-CN/ssot/Feature-SSoT.md +0 -49
  177. package/templates-zh-CN/ssot/Lessons-SSoT.md +0 -49
@@ -6,26 +6,19 @@ import path from "node:path";
6
6
  import { createInterface } from "node:readline/promises";
7
7
  import {
8
8
  addCapability,
9
- buildMigrationPlan,
10
9
  buildStatus,
11
- confirmTaskReview,
12
- createTask,
13
10
  doctorUserSkill,
14
11
  installUserSkill,
15
- listLifecycleTasks,
16
12
  normalizeLocale,
17
- promoteLessonCandidate,
18
- runMigration,
13
+ rebuildGovernanceIndexes,
19
14
  serveDashboardWorkbench,
20
15
  validateSourcePackageBoundary,
21
- updateModuleStep,
22
- updateTaskPhase,
23
- updateTaskLifecycle,
24
- verifyMigrationSession,
25
- writeDashboardFolder,
26
- writeDashboardSingleFile,
27
16
  writeInitFiles,
28
17
  } from "./lib/harness-core.mjs";
18
+ import { runDashboardCommand } from "./commands/dashboard-command.mjs";
19
+ import { runMigrationCommand } from "./commands/migration-command.mjs";
20
+ import { runPresetCommand } from "./commands/preset-command.mjs";
21
+ import { runTaskCommand } from "./commands/task-command.mjs";
29
22
 
30
23
  const args = process.argv.slice(2);
31
24
  const command = args.shift() || "help";
@@ -96,22 +89,47 @@ Usage:
96
89
  harness migrate-plan [--json] [--limit n] [target]
97
90
  harness migrate-run [--locale zh-CN|en-US] [--assume-locale] [--allow-dirty] [--plan-only] [--out-dir folder] [--session-dir folder] [target]
98
91
  harness migrate-verify [--json] [--full-cutover] <session.json>
99
- harness new-task <task-id> [--module key] [--budget simple|standard|complex] [--preset legacy-migration] [--from-session session.json] [--long-running] [--title title] [--locale zh-CN|en-US] [--dry-run] [target]
92
+ harness governance rebuild [--dry-run] [--archive] [--apply] [target]
93
+ harness preset list [--json] [target]
94
+ harness preset inspect <id> [--json] [target]
95
+ harness preset check <id> [--json] [target]
96
+ harness preset install <path-or-builtin-id> [--project] [--force] [--json] [target]
97
+ harness preset seed [--project] [--force] [--dry-run] [--json] [target]
98
+ harness preset uninstall <id> [--project] [--json] [target]
99
+ harness new-task <task-id> [--module key] [--budget simple|standard|complex] [--preset id] [--from-session session.json] [--long-running] [--title title] [--locale zh-CN|en-US] [--dry-run] [target]
100
100
  harness task-start <task-id> [--message text] [target]
101
101
  harness task-phase <task-id> <phase-id> [--state done] [--completion 100] [--evidence present] [target]
102
102
  harness task-log <task-id> --message text [--evidence type:PATH:summary] [target]
103
103
  harness task-block <task-id> [--message text] [target]
104
104
  harness task-review <task-id> [--message text] [target]
105
105
  harness review-confirm <task-id> --confirm task-id [--reviewer name] [--message text] [target]
106
- harness lesson-promote <task-id> <candidate-id> [--dry-run] [target]
106
+ harness lesson-promote <task-id> <candidate-id> [--dry-run|--apply] [target]
107
+ harness lesson-sediment <task-id> <candidate-id> [--dry-run] [--title title] [target]
107
108
  harness task-complete <task-id> [--message text] [target]
108
- harness task-list [--json] [--state state] [--module key] [target]
109
+ harness task-list [--json] [--state state] [--module key] [--queue queue] [--preset id] [--review status] [--lesson status] [--missing-materials] [--search text] [target]
110
+ harness task-index [--json] [target]
111
+ harness task-supersede <old-task-id> --by <new-task-id> [--reason text] [target]
112
+ harness task-delete <task-id> --soft [--reason text] [target]
113
+ harness task-archive <task-id> [--reason text] [target]
114
+ harness task-reopen <task-id> [--reason text] [target]
109
115
  harness module-step <module-key> <step-id> [--state done|in-progress|blocked] [target]
110
- harness install-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir] [--dry-run] [--force] [--yes]
116
+ harness install-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir] [--dry-run] [--force] [--skip-presets] [--yes]
111
117
  harness doctor-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir]
112
118
 
113
119
  If init runs in an interactive terminal and --locale is omitted, it asks for a
114
120
  language. Non-interactive init defaults to en-US.
121
+
122
+ Preset discovery:
123
+ Project presets live in <target>/.coding-agent-harness/presets/<preset-id>/.
124
+ User presets live in ~/.coding-agent-harness/presets/<preset-id>/.
125
+ Harness discovers project presets first when a target is supplied, then user
126
+ presets, then bundled package presets under presets/<preset-id>/.
127
+ "harness init" seeds bundled presets into the target project. "harness
128
+ install-user" and npm postinstall seed bundled presets into the user root.
129
+ Use "harness preset seed" to repair or re-run preset seeding.
130
+ Use "harness preset list --json" to see available presets, their source,
131
+ purpose, compatible budgets, and manifest path. Use "harness preset inspect
132
+ <id> --json" for the full preset manifest summary.
115
133
  `);
116
134
  }
117
135
 
@@ -123,6 +141,8 @@ function exitWithReport(report) {
123
141
 
124
142
  if (command === "help" || command === "--help" || command === "-h") {
125
143
  printHelp();
144
+ } else if (args[0] === "help" || args.includes("--help") || args.includes("-h")) {
145
+ printHelp();
126
146
  } else if (command === "check") {
127
147
  const profile = takeOption("--profile", "target-project");
128
148
  const strict = takeFlag("--strict");
@@ -166,8 +186,9 @@ if (command === "help" || command === "--help" || command === "-h") {
166
186
  const port = takeOption("--port", "0");
167
187
  const localeOverride = takeOption("--locale", "");
168
188
  const target = targetArg();
189
+ const usesDefaultOutDir = !outDir;
169
190
  const dashboardOutDir = outDir || defaultDevOutDir(target);
170
- const opts = localeOverride ? { localeOverride } : {};
191
+ const opts = { ...(localeOverride ? { localeOverride } : {}), recoverGeneratedDashboard: usesDefaultOutDir };
171
192
  try {
172
193
  await serveDashboardWorkbench(dashboardOutDir, target, { ...opts, host, port, autoRefresh: true, open, label: "harness dev" });
173
194
  } catch (error) {
@@ -175,63 +196,7 @@ if (command === "help" || command === "--help" || command === "-h") {
175
196
  process.exit(1);
176
197
  }
177
198
  } else if (command === "dashboard") {
178
- const watch = takeFlag("--watch");
179
- const workbench = takeFlag("--workbench");
180
- const out = takeOption("--out", "harness-dashboard.html");
181
- const outDir = takeOption("--out-dir", "");
182
- const host = takeOption("--host", "127.0.0.1");
183
- const port = takeOption("--port", "0");
184
- const localeOverride = takeOption("--locale", "");
185
- const opts = localeOverride ? { localeOverride } : {};
186
- if (workbench) {
187
- if (!outDir) {
188
- console.error("dashboard --workbench requires --out-dir so regenerated data has a stable folder");
189
- process.exit(2);
190
- }
191
- try {
192
- await serveDashboardWorkbench(outDir, targetArg(), { ...opts, host, port });
193
- } catch (error) {
194
- console.error(error.message);
195
- process.exit(1);
196
- }
197
- }
198
- if (watch) {
199
- if (!outDir) {
200
- console.error("dashboard --watch requires --out-dir so updates are written to a stable folder");
201
- process.exit(2);
202
- }
203
- const target = targetArg();
204
- const docsRoot = path.basename(path.resolve(target)) === "docs" ? path.resolve(target) : path.join(path.resolve(target), "docs");
205
- const regenerate = () => {
206
- try {
207
- console.log(writeDashboardFolder(outDir, target, opts));
208
- console.log(`dashboard regenerated: ${new Date().toISOString()}`);
209
- } catch (error) {
210
- console.error(`dashboard regeneration failed: ${error.message}`);
211
- }
212
- };
213
- regenerate();
214
- let timer = null;
215
- const watcher = fs.watch(docsRoot, { recursive: true }, () => {
216
- clearTimeout(timer);
217
- timer = setTimeout(regenerate, 300);
218
- });
219
- const close = () => {
220
- watcher.close();
221
- clearTimeout(timer);
222
- process.exit(0);
223
- };
224
- process.on("SIGINT", close);
225
- process.on("SIGTERM", close);
226
- console.log(`watching ${docsRoot}`);
227
- await new Promise(() => {});
228
- }
229
- if (outDir) {
230
- console.log(writeDashboardFolder(outDir, targetArg(), opts));
231
- } else {
232
- console.log(writeDashboardSingleFile(out, targetArg(), opts));
233
- }
234
- process.exit(0);
199
+ await runDashboardCommand({ takeFlag, takeOption, targetArg });
235
200
  } else if (command === "init") {
236
201
  const dryRun = takeFlag("--dry-run");
237
202
  const addNpmScripts = takeFlag("--add-npm-scripts");
@@ -239,7 +204,7 @@ if (command === "help" || command === "--help" || command === "-h") {
239
204
  const capabilities = takeOption("--capabilities", "core").split(",").map((item) => item.trim()).filter(Boolean);
240
205
  try {
241
206
  const result = writeInitFiles(targetArg(), capabilities, { dryRun, locale, addNpmScripts });
242
- console.log(JSON.stringify({ dryRun, locale: result.locale, capabilities: result.capabilities, changes: result.changes, nextCommands: result.nextCommands, report: result.report }, null, 2));
207
+ console.log(JSON.stringify({ dryRun, locale: result.locale, capabilities: result.capabilities, changes: result.changes, presetSeed: result.presetSeed, nextCommands: result.nextCommands, report: result.report }, null, 2));
243
208
  } catch (error) {
244
209
  console.error(error.message);
245
210
  process.exit(1);
@@ -259,202 +224,32 @@ if (command === "help" || command === "--help" || command === "-h") {
259
224
  console.error(error.message);
260
225
  process.exit(1);
261
226
  }
262
- } else if (command === "migrate-plan") {
263
- const json = takeFlag("--json");
264
- const limit = Number.parseInt(takeOption("--limit", "20"), 10) || 20;
265
- try {
266
- const plan = buildMigrationPlan(targetArg(), { limit });
267
- if (json) {
268
- console.log(JSON.stringify(plan, null, 2));
269
- } else {
270
- console.log(`Migration Plan: ${plan.target}`);
271
- console.log(`mode: ${plan.mode}`);
272
- console.log(`warnings: ${plan.summary.warnings}`);
273
- console.log(`task actions: ${plan.summary.taskActions}`);
274
- console.log(`visual map actions: ${plan.summary.visualMapActions}`);
275
- console.log(`legacy visual-only tasks: ${plan.summary.legacyVisualOnly}`);
276
- console.log(`weak briefs: ${plan.summary.weakBrief}`);
277
- console.log(`unknown classifications: ${plan.summary.unknownClassification}`);
278
- console.log(`full cutover eligible: ${plan.summary.fullCutoverEligible ? "yes" : "no"}`);
279
- console.log(`review actions: ${plan.summary.reviewSchemaGaps}`);
280
- console.log(`legacy actions: ${plan.summary.legacyReferenceGaps}`);
281
- console.log(`legacy residuals: ${plan.summary.legacyResiduals}`);
282
- console.log(`recommended capabilities: ${plan.summary.recommendedCapabilities.join(", ") || "none"}`);
283
- console.log("\nPhases:");
284
- for (const phase of plan.phases) console.log(`- ${phase.id}: ${phase.title}`);
285
- console.log("\nTop task actions:");
286
- for (const action of plan.taskActions) console.log(`- ${action.taskId}: add ${action.files.join(", ")}`);
287
- console.log("\nTop review actions:");
288
- for (const action of plan.reviewActions) console.log(`- ${action.path}: add ${action.missing.join(", ")}`);
289
- console.log("\nTop legacy residuals:");
290
- for (const action of plan.legacyResiduals) console.log(`- ${action.taskId}: ${action.missing} (${action.reason})`);
291
- console.log("\nNext commands:");
292
- for (const next of plan.nextCommands) console.log(`- ${next}`);
293
- }
294
- } catch (error) {
295
- console.error(error.message);
296
- process.exit(1);
297
- }
298
- } else if (command === "migrate-run") {
299
- const locale = takeOption("--locale", "");
300
- const assumeLocale = takeFlag("--assume-locale");
301
- const allowDirty = takeFlag("--allow-dirty");
302
- const planOnly = takeFlag("--plan-only");
303
- const outDir = takeOption("--out-dir", "");
304
- const sessionDir = takeOption("--session-dir", "");
305
- try {
306
- console.log(
307
- JSON.stringify(
308
- runMigration(targetArg(), {
309
- locale,
310
- assumeLocale,
311
- allowDirty,
312
- planOnly,
313
- outDir,
314
- sessionDir,
315
- }),
316
- null,
317
- 2,
318
- ),
319
- );
320
- } catch (error) {
321
- console.error(error.message);
322
- process.exit(1);
323
- }
324
- } else if (command === "migrate-verify") {
325
- const json = takeFlag("--json");
326
- const fullCutover = takeFlag("--full-cutover");
327
- const sessionPath = args.shift();
328
- if (!sessionPath) {
329
- console.error("Missing session.json path");
227
+ } else if (["migrate-plan", "migrate-run", "migrate-verify"].includes(command)) {
228
+ runMigrationCommand(command, { args, takeFlag, takeOption, targetArg });
229
+ } else if (command === "governance") {
230
+ const subcommand = args.shift() || "";
231
+ if (subcommand !== "rebuild") {
232
+ console.error(`Unknown governance subcommand: ${subcommand || "(missing)"}`);
330
233
  process.exit(2);
331
234
  }
332
- const result = verifyMigrationSession(sessionPath, { fullCutover });
333
- if (json) {
334
- console.log(JSON.stringify(result, null, 2));
335
- } else {
336
- for (const failure of result.failures) console.error(`Failure: ${failure}`);
337
- for (const warning of result.warnings) console.log(`Warning: ${warning}`);
338
- console.log(`Migration verify ${result.status}: ${result.sessionPath}`);
339
- }
340
- process.exit(result.status === "pass" ? 0 : 1);
341
- } else if (command === "new-task") {
342
235
  const dryRun = takeFlag("--dry-run");
343
- const locale = takeOption("--locale", "");
344
- const title = takeOption("--title", "");
345
- const moduleKey = takeOption("--module", "");
346
- const budget = takeOption("--budget", "standard");
347
- const preset = takeOption("--preset", "");
348
- const fromSession = takeOption("--from-session", "");
349
- const longRunning = takeFlag("--long-running");
350
- const taskId = args.shift() || (fromSession ? "harness-v1-migration" : "");
351
- if (!taskId) {
352
- console.error("Missing task id");
353
- process.exit(2);
354
- }
355
- try {
356
- console.log(JSON.stringify(createTask(targetArg(), taskId, { title, locale, dryRun, moduleKey, budget, longRunning, preset, fromSession }), null, 2));
357
- } catch (error) {
358
- console.error(error.message);
359
- process.exit(1);
360
- }
361
- } else if (command === "task-phase") {
362
- const state = takeOption("--state", "");
363
- const completion = takeOption("--completion", "");
364
- const evidenceStatus = takeOption("--evidence", "");
365
- const taskId = args.shift();
366
- const phaseId = args.shift();
367
- if (!taskId || !phaseId) {
368
- console.error("Missing task id or phase id");
369
- process.exit(2);
370
- }
371
- try {
372
- console.log(JSON.stringify(updateTaskPhase(targetArg(), taskId, phaseId, { state, completion, evidenceStatus }), null, 2));
373
- } catch (error) {
374
- console.error(error.message);
375
- process.exit(1);
376
- }
377
- } else if (["task-start", "task-log", "task-block", "task-review", "task-complete"].includes(command)) {
378
- const message = takeOption("--message", "");
379
- const evidence = takeOption("--evidence", "");
380
- const taskId = args.shift();
381
- if (!taskId) {
382
- console.error("Missing task id");
383
- process.exit(2);
384
- }
385
- const lifecycle = {
386
- "task-start": { event: "task-start", state: "in_progress" },
387
- "task-log": { event: "task-log", state: "" },
388
- "task-block": { event: "task-block", state: "blocked" },
389
- "task-review": { event: "task-review", state: "review" },
390
- "task-complete": { event: "task-complete", state: "done" },
391
- }[command];
392
- try {
393
- console.log(JSON.stringify(updateTaskLifecycle(targetArg(), taskId, { ...lifecycle, message, evidence }), null, 2));
394
- } catch (error) {
395
- console.error(error.message);
396
- process.exit(1);
397
- }
398
- } else if (command === "review-confirm") {
399
- const reviewer = takeOption("--reviewer", "Human Reviewer");
400
- const message = takeOption("--message", "");
401
- const evidence = takeOption("--evidence", "");
402
- const confirmText = takeOption("--confirm", "");
403
- const taskId = args.shift();
404
- if (!taskId) {
405
- console.error("Missing task id");
406
- process.exit(2);
407
- }
408
- try {
409
- console.log(JSON.stringify(confirmTaskReview(targetArg(), taskId, { reviewer, message, evidence, confirmText }), null, 2));
410
- } catch (error) {
411
- console.error(error.message);
412
- process.exit(1);
413
- }
414
- } else if (command === "lesson-promote") {
415
- const dryRun = takeFlag("--dry-run");
416
- const taskId = args.shift();
417
- const candidateId = args.shift();
418
- if (!taskId || !candidateId) {
419
- console.error("Missing task id or candidate id");
420
- process.exit(2);
421
- }
422
- try {
423
- console.log(JSON.stringify(promoteLessonCandidate(targetArg(), taskId, candidateId, { dryRun }), null, 2));
424
- } catch (error) {
425
- console.error(error.message);
426
- process.exit(1);
427
- }
428
- } else if (command === "task-list") {
429
- const json = takeFlag("--json");
430
- const state = takeOption("--state", "");
431
- const moduleKey = takeOption("--module", "");
432
- const result = listLifecycleTasks(targetArg(), { state, moduleKey });
433
- if (json) {
434
- console.log(JSON.stringify(result, null, 2));
435
- } else {
436
- for (const task of result.tasks) {
437
- console.log(`${task.id}\t${task.state}\t${task.completion}%\t${task.title}`);
438
- }
439
- }
440
- } else if (command === "module-step") {
441
- const state = takeOption("--state", "done");
442
- const moduleKey = args.shift();
443
- const stepId = args.shift();
444
- if (!moduleKey || !stepId) {
445
- console.error("Missing module key or step id");
446
- process.exit(2);
447
- }
236
+ const archive = takeFlag("--archive");
237
+ const apply = takeFlag("--apply");
448
238
  try {
449
- console.log(JSON.stringify(updateModuleStep(targetArg(), moduleKey, stepId, { state }), null, 2));
239
+ console.log(JSON.stringify(rebuildGovernanceIndexes(targetArg(), { dryRun, archive, apply }), null, 2));
450
240
  } catch (error) {
451
241
  console.error(error.message);
452
242
  process.exit(1);
453
243
  }
244
+ } else if (command === "preset") {
245
+ runPresetCommand({ args, takeFlag, targetArg });
246
+ } else if (["new-task", "task-phase", "task-start", "task-log", "task-block", "task-review", "task-complete", "review-confirm", "lesson-promote", "lesson-sediment", "task-list", "task-index", "task-supersede", "task-delete", "task-archive", "task-reopen", "module-step"].includes(command)) {
247
+ runTaskCommand(command, { args, takeFlag, takeOption, targetArg });
454
248
  } else if (command === "install-user") {
455
249
  const dryRun = takeFlag("--dry-run");
456
250
  const force = takeFlag("--force");
457
251
  const yes = takeFlag("--yes") || takeFlag("-y");
252
+ const skipPresets = takeFlag("--skip-presets");
458
253
  takeFlag("--global");
459
254
  const agent = takeOption("--agent", "codex");
460
255
  const home = takeOption("--home", "");
@@ -463,7 +258,7 @@ if (command === "help" || command === "--help" || command === "-h") {
463
258
  process.exit(2);
464
259
  }
465
260
  try {
466
- console.log(JSON.stringify(installUserSkill({ agent, home, dryRun, force }), null, 2));
261
+ console.log(JSON.stringify(installUserSkill({ agent, home, dryRun, force, seedPresets: !skipPresets }), null, 2));
467
262
  } catch (error) {
468
263
  console.error(error.message);
469
264
  process.exit(1);
@@ -14,7 +14,9 @@ import {
14
14
  walkFiles,
15
15
  normalizeLocale,
16
16
  localizedTemplateSource,
17
+ userPresetRootForHome,
17
18
  } from "./core-shared.mjs";
19
+ import { listBundledPresetIds, seedBundledPresets } from "./preset-registry.mjs";
18
20
 
19
21
  export const capabilityDefinitions = {
20
22
  core: {
@@ -124,12 +126,53 @@ export function validateSourcePackageBoundary(targetInput = ".") {
124
126
  .split("\0")
125
127
  .filter(Boolean)
126
128
  .filter((file) => file === "AGENTS.md" || file === "CLAUDE.md" || file === "docs" || file.startsWith("docs/") || file === ".harness-private" || file.startsWith(".harness-private/"));
129
+ const tracked = spawnSync("git", ["-C", root, "ls-files", "-z", "--", "harness-dashboard.html"], { encoding: "utf8" });
130
+ const generatedRootDashboard = tracked.status === 0
131
+ ? tracked.stdout.split("\0").filter(Boolean)
132
+ .filter((file) => fs.existsSync(path.join(root, file)))
133
+ : [];
134
+ const internalScripts = ["scripts/test-harness.mjs", "scripts/smoke-dashboard.mjs"]
135
+ .filter((file) => fs.existsSync(path.join(root, file)));
136
+ const dashboardAppDrift = validateDashboardAppAssembly(root);
137
+ const dashboardCssDrift = validateDashboardAssetAssembly(root, "app.css.manifest.json", "app.css", "dashboard assets/app.css does not match css-src manifest assembly");
127
138
  return {
128
- failures: localOnly.map((file) => `private local-only file staged: ${file}`),
129
- warnings: [],
139
+ failures: [
140
+ ...localOnly.map((file) => `private local-only file staged: ${file}`),
141
+ ...generatedRootDashboard.map((file) => `generated dashboard file tracked in source root: ${file}`),
142
+ ...internalScripts.map((file) => `internal test/smoke file in publishable scripts directory: ${file}`),
143
+ ...dashboardAppDrift,
144
+ ...dashboardCssDrift,
145
+ ],
146
+ warnings: tracked.status === 0 ? [] : [`could not inspect tracked generated dashboard files: ${tracked.stderr.trim() || tracked.status}`],
130
147
  };
131
148
  }
132
149
 
150
+ function validateDashboardAppAssembly(root) {
151
+ return validateDashboardAssetAssembly(root, "app.manifest.json", "app.js", "dashboard assets/app.js does not match app-src manifest assembly");
152
+ }
153
+
154
+ function validateDashboardAssetAssembly(root, manifestName, assetName, driftMessage) {
155
+ const assetsDir = path.join(root, "templates/dashboard/assets");
156
+ const manifestPath = path.join(assetsDir, manifestName);
157
+ const assetPath = path.join(assetsDir, assetName);
158
+ if (!fs.existsSync(manifestPath) || !fs.existsSync(assetPath)) return [];
159
+ try {
160
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
161
+ if (!Array.isArray(manifest) || manifest.length === 0) {
162
+ return [`dashboard asset manifest must list source files: ${manifestName}`];
163
+ }
164
+ const assembled = `${manifest.map((relativePath) => {
165
+ const source = path.join(assetsDir, relativePath);
166
+ if (!fs.existsSync(source)) throw new Error(`missing ${relativePath}`);
167
+ return fs.readFileSync(source, "utf8").trimEnd();
168
+ }).join("\n\n")}\n`;
169
+ const trackedAsset = fs.readFileSync(assetPath, "utf8");
170
+ return trackedAsset === assembled ? [] : [driftMessage];
171
+ } catch (error) {
172
+ return [`could not validate dashboard asset assembly (${assetName}): ${error.message}`];
173
+ }
174
+ }
175
+
133
176
  export function detectCapabilities(target) {
134
177
  const detected = new Set(["core"]);
135
178
  if (existsInDocs(target, "09-PLANNING/Module-Registry.md")) detected.add("module-parallel");
@@ -164,6 +207,7 @@ export function buildInstallReport({ target, locale, capabilities, changes, dryR
164
207
  agentInstructions: [
165
208
  "Agents must choose locale during Decide and pass --locale zh-CN|en-US explicitly in non-interactive installs.",
166
209
  "Use core for every install; add optional capabilities only when their selectWhen rule is true.",
210
+ "Bundled presets are seeded during init; use harness preset list --json before choosing task presets.",
167
211
  "After scaffold, run Configure before marking capabilities configured or verified.",
168
212
  "Run harness check/status/dashboard and record residuals before delivery.",
169
213
  ],
@@ -209,6 +253,7 @@ function skillPackageEntries() {
209
253
  "references",
210
254
  "templates",
211
255
  "templates-zh-CN",
256
+ "presets",
212
257
  "scripts",
213
258
  "docs-release",
214
259
  "examples",
@@ -246,7 +291,7 @@ function copySkillPackage(targetRoot, { dryRun = false, force = false } = {}) {
246
291
  return changes;
247
292
  }
248
293
 
249
- export function installUserSkill({ agent = "codex", home = "", dryRun = false, force = false } = {}) {
294
+ export function installUserSkill({ agent = "codex", home = "", dryRun = false, force = false, seedPresets = true } = {}) {
250
295
  const agents = normalizeUserAgent(agent);
251
296
  const targets = agents.map((targetAgent) => {
252
297
  const target = targetForUserAgent(targetAgent, home);
@@ -260,8 +305,11 @@ export function installUserSkill({ agent = "codex", home = "", dryRun = false, f
260
305
  skipped: changes.filter((change) => change.action === "skip-existing").length,
261
306
  };
262
307
  });
263
- const changed = targets.some((target) => target.created > 0 || target.overwritten > 0);
264
- const onlySkipped = targets.every((target) => target.created === 0 && target.overwritten === 0 && target.skipped > 0);
308
+ const presetSeed = seedPresets ? seedBundledPresets({ scope: "user", home, dryRun, force }) : null;
309
+ const changed = targets.some((target) => target.created > 0 || target.overwritten > 0) || (presetSeed && (presetSeed.created > 0 || presetSeed.overwritten > 0));
310
+ const onlySkipped =
311
+ targets.every((target) => target.created === 0 && target.overwritten === 0 && target.skipped > 0) &&
312
+ (!presetSeed || presetSeed.presets.every((preset) => preset.action === "skip-existing"));
265
313
  return {
266
314
  operation: "install-user",
267
315
  status: dryRun ? "dry-run" : changed ? "installed" : onlySkipped ? "already-present" : "no-op",
@@ -269,6 +317,7 @@ export function installUserSkill({ agent = "codex", home = "", dryRun = false, f
269
317
  force,
270
318
  version: packageVersion(),
271
319
  source: repoRoot,
320
+ presets: presetSeed,
272
321
  targets,
273
322
  };
274
323
  }
@@ -301,6 +350,7 @@ export function doctorUserSkill({ agent = "codex", home = "" } = {}) {
301
350
  "references",
302
351
  "templates",
303
352
  "templates-zh-CN",
353
+ "presets",
304
354
  "scripts/harness.mjs",
305
355
  "docs-release/guides/agent-installation.md",
306
356
  ];
@@ -315,12 +365,20 @@ export function doctorUserSkill({ agent = "codex", home = "" } = {}) {
315
365
  missing,
316
366
  };
317
367
  });
368
+ const presetRoot = userPresetRootForHome(home);
369
+ const missingPresets = listBundledPresetIds().filter((id) => !fs.existsSync(path.join(presetRoot, id, "preset.yaml")));
370
+ const presets = {
371
+ root: presetRoot,
372
+ status: missingPresets.length === 0 ? "pass" : "fail",
373
+ missing: missingPresets,
374
+ };
318
375
  const harnessCommand = commandOnPath("harness");
319
376
  return {
320
377
  operation: "doctor-user",
321
- status: targets.every((target) => target.status === "pass") ? "pass" : "fail",
378
+ status: targets.every((target) => target.status === "pass") && presets.status === "pass" ? "pass" : "fail",
322
379
  version: packageVersion(),
323
380
  harnessCommand: harnessCommand || null,
381
+ presets,
324
382
  targets,
325
383
  };
326
384
  }
@@ -399,7 +457,6 @@ export function plannedInitFiles(capabilities = ["core"], { locale = "en-US" } =
399
457
  ["docs/09-PLANNING/TASKS/_task-template/review.md", "templates/planning/review.md"],
400
458
  ["docs/05-TEST-QA/Regression-SSoT.md", "templates/ssot/Regression-SSoT.md"],
401
459
  ["docs/05-TEST-QA/Cadence-Ledger.md", "templates/regression/Cadence-Ledger.md"],
402
- ["docs/01-GOVERNANCE/Lessons-SSoT.md", "templates/ssot/Lessons-SSoT.md"],
403
460
  ["docs/10-WALKTHROUGH/_walkthrough-template.md", "templates/walkthrough/walkthrough-template.md"],
404
461
  ["docs/10-WALKTHROUGH/Closeout-SSoT.md", "templates/walkthrough/Closeout-SSoT.md"],
405
462
  ["docs/11-REFERENCE/external-source-intake-standard.md", "templates/reference/external-source-intake-standard.md"],
@@ -464,8 +521,9 @@ export function writeInitFiles(targetInput, capabilities, { dryRun = true, local
464
521
  const registryPath = path.join(target.projectRoot, ".harness-capabilities.json");
465
522
  if (!fs.existsSync(registryPath)) fs.writeFileSync(registryPath, `${JSON.stringify(registry, null, 2)}\n`);
466
523
  }
524
+ const presetSeed = seedBundledPresets({ scope: "project", targetInput: target.projectRoot, dryRun });
467
525
  const report = buildInstallReport({ target, locale: normalizedLocale, capabilities: normalizedCapabilities, changes, dryRun, operation: "init" });
468
- return { target, capabilities: normalizedCapabilities, locale: normalizedLocale, changes, nextCommands: initNextCommands(), report };
526
+ return { target, capabilities: normalizedCapabilities, locale: normalizedLocale, changes, presetSeed, nextCommands: initNextCommands(), report };
469
527
  }
470
528
 
471
529
  function initNextCommands() {