coding-agent-harness 1.0.2 → 1.0.5
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.
- package/CHANGELOG.md +32 -0
- package/CONTRIBUTING.md +98 -0
- package/LICENSE +661 -21
- package/LICENSE-EXCEPTION.md +37 -0
- package/README.md +244 -87
- package/README.zh-CN.md +77 -35
- package/SKILL.md +32 -24
- package/docs-release/README.md +9 -5
- package/docs-release/architecture/overview.md +17 -5
- package/docs-release/architecture/overview.zh-CN.md +9 -5
- package/docs-release/architecture/system-explainer/01-system-overview.md +217 -0
- package/docs-release/architecture/system-explainer/02-module-dependency.md +257 -0
- package/docs-release/architecture/system-explainer/03-task-lifecycle.md +304 -0
- package/docs-release/architecture/system-explainer/04-check-and-governance.md +239 -0
- package/docs-release/architecture/system-explainer/05-data-flow.md +276 -0
- package/docs-release/architecture/system-explainer/06-preset-and-migration.md +303 -0
- package/docs-release/architecture/system-explainer/README.md +67 -0
- package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +226 -0
- package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +263 -0
- package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +319 -0
- package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +250 -0
- package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +290 -0
- package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +323 -0
- package/docs-release/architecture/system-explainer/en-US/README.md +70 -0
- package/docs-release/assets/dashboard-overview.png +0 -0
- package/docs-release/guides/agent-installation.en-US.md +39 -15
- package/docs-release/guides/agent-installation.md +43 -16
- package/docs-release/guides/contributing.md +100 -0
- package/docs-release/guides/contributing.zh-CN.md +99 -0
- package/docs-release/guides/document-audience-and-surfaces.en-US.md +3 -2
- package/docs-release/guides/document-audience-and-surfaces.md +3 -2
- package/docs-release/guides/full-legacy-migration-subagent-strategy.md +2 -2
- package/docs-release/guides/full-legacy-migration-subagent-strategy.zh-CN.md +2 -2
- package/docs-release/guides/legacy-migration-agent-prompt.md +0 -11
- package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +0 -11
- package/docs-release/guides/migration-playbook.en-US.md +14 -15
- package/docs-release/guides/migration-playbook.md +14 -15
- package/docs-release/guides/parent-control-repository-pattern.en-US.md +7 -5
- package/docs-release/guides/parent-control-repository-pattern.md +7 -5
- package/docs-release/guides/preset-development.md +238 -0
- package/docs-release/guides/repository-operating-models.en-US.md +5 -4
- package/docs-release/guides/repository-operating-models.md +5 -4
- package/docs-release/guides/task-state-machine.en-US.md +224 -0
- package/docs-release/guides/task-state-machine.md +231 -0
- package/docs-release/intl/en-US.md +1 -1
- package/docs-release/intl/zh-CN.md +1 -1
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/INDEX.md +60 -0
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/findings.md +7 -0
- package/package.json +10 -4
- package/presets/legacy-migration/checks/preset-check.mjs +3 -0
- package/presets/legacy-migration/preset.yaml +134 -0
- package/presets/legacy-migration/scripts/plan-work-queue.mjs +4 -0
- package/presets/legacy-migration/scripts/scaffold-task-contracts.mjs +4 -0
- package/presets/legacy-migration/templates/execution_strategy.append.md +18 -0
- package/presets/legacy-migration/templates/findings.seed.md +17 -0
- package/presets/legacy-migration/templates/review.seed.md +12 -0
- package/presets/legacy-migration/templates/task_plan.append.md +9 -0
- package/presets/legacy-migration/templates/visual_map.append.md +12 -0
- package/presets/legacy-migration/workbench/dashboard-panels.yaml +2 -0
- package/presets/legacy-migration/workbench/migration-queue.schema.json +23 -0
- package/presets/lesson-sedimentation/preset.yaml +23 -0
- package/presets/lesson-sedimentation/templates/prompt.md +23 -0
- package/presets/module/preset.yaml +25 -0
- package/presets/module/templates/execution_strategy.append.md +8 -0
- package/presets/module/templates/task_plan.append.md +17 -0
- package/presets/standard-task/preset.yaml +31 -0
- package/presets/standard-task/templates/task_plan.append.md +7 -0
- package/references/adversarial-review-standard.md +2 -2
- package/references/agents-md-pattern.md +2 -2
- package/references/delivery-operating-model-standard.md +3 -3
- package/references/docs-directory-standard.md +6 -7
- package/references/harness-ledger.md +53 -96
- package/references/lessons-governance.md +88 -93
- package/references/module-parallel-standard.md +14 -14
- package/references/planning-loop.md +12 -6
- package/references/pull-request-standard.md +118 -0
- package/references/repo-governance-standard.md +11 -2
- package/references/review-routing-standard.md +7 -1
- package/references/ssot-governance.md +67 -59
- package/references/taskr-gap-analysis.md +600 -0
- package/references/walkthrough-closeout.md +7 -7
- package/scripts/check-harness.mjs +40 -301
- package/scripts/commands/dashboard-command.mjs +67 -0
- package/scripts/commands/migration-command.mjs +126 -0
- package/scripts/commands/preset-command.mjs +73 -0
- package/scripts/commands/task-command.mjs +328 -0
- package/scripts/harness.mjs +59 -260
- package/scripts/lib/capability-registry.mjs +82 -28
- package/scripts/lib/check-module-parallel.mjs +230 -0
- package/scripts/lib/check-profiles.mjs +90 -228
- package/scripts/lib/check-task-contracts.mjs +55 -0
- package/scripts/lib/core-shared.mjs +65 -2
- package/scripts/lib/dashboard-data.mjs +155 -24
- package/scripts/lib/dashboard-workbench.mjs +131 -12
- package/scripts/lib/dashboard-writer.mjs +20 -4
- package/scripts/lib/git-status-summary.mjs +46 -0
- package/scripts/lib/governance-index-generator.mjs +174 -0
- package/scripts/lib/governance-sync.mjs +611 -0
- package/scripts/lib/governance-table-boundary.mjs +175 -0
- package/scripts/lib/harness-core.mjs +6 -0
- package/scripts/lib/lesson-maintenance.mjs +36 -29
- package/scripts/lib/markdown-utils.mjs +33 -0
- package/scripts/lib/migration-planner.mjs +4 -6
- package/scripts/lib/migration-support.mjs +1 -1
- package/scripts/lib/phase-kind.mjs +50 -0
- package/scripts/lib/preset-audit-contracts.mjs +37 -0
- package/scripts/lib/preset-engine.mjs +494 -0
- package/scripts/lib/preset-registry.mjs +776 -0
- package/scripts/lib/preset-resource-contracts.mjs +83 -0
- package/scripts/lib/review-confirm-git-gate.mjs +248 -0
- package/scripts/lib/status-builder.mjs +88 -0
- package/scripts/lib/status-dashboard-renderer.mjs +105 -0
- package/scripts/lib/subagent-authorization-audit.mjs +196 -0
- package/scripts/lib/task-audit-metadata.mjs +385 -0
- package/scripts/lib/task-audit-migration.mjs +350 -0
- package/scripts/lib/task-completion-consistency.mjs +26 -0
- package/scripts/lib/task-index.mjs +93 -0
- package/scripts/lib/task-lesson-candidates.mjs +242 -0
- package/scripts/lib/task-lesson-sedimentation.mjs +326 -0
- package/scripts/lib/task-lifecycle/create-task-helpers.mjs +67 -0
- package/scripts/lib/task-lifecycle/phase-sync.mjs +88 -0
- package/scripts/lib/task-lifecycle/review-confirm.mjs +112 -0
- package/scripts/lib/task-lifecycle/review-gates.mjs +73 -0
- package/scripts/lib/task-lifecycle/review-submission.mjs +63 -0
- package/scripts/lib/task-lifecycle/scaffold-provenance.mjs +49 -0
- package/scripts/lib/task-lifecycle/template-files.mjs +53 -0
- package/scripts/lib/task-lifecycle/text-utils.mjs +24 -0
- package/scripts/lib/task-lifecycle.mjs +338 -477
- package/scripts/lib/task-metadata.mjs +118 -0
- package/scripts/lib/task-review-model.mjs +455 -0
- package/scripts/lib/task-scanner.mjs +193 -372
- package/scripts/lib/task-tombstone-commands.mjs +140 -0
- package/scripts/postinstall.mjs +14 -0
- package/skills/preset-creator/SKILL.md +179 -0
- package/skills/preset-creator/references/complex-task-skeleton/README.md +31 -0
- package/skills/preset-creator/references/complex-task-skeleton/artifacts/INDEX.md +12 -0
- package/skills/preset-creator/references/complex-task-skeleton/brief.md +43 -0
- package/skills/preset-creator/references/complex-task-skeleton/execution_strategy.md +71 -0
- package/skills/preset-creator/references/complex-task-skeleton/findings.md +24 -0
- package/skills/preset-creator/references/complex-task-skeleton/lesson_candidates.md +70 -0
- package/skills/preset-creator/references/complex-task-skeleton/long-running-task-contract.md +76 -0
- package/skills/preset-creator/references/complex-task-skeleton/progress.md +33 -0
- package/skills/preset-creator/references/complex-task-skeleton/references/INDEX.md +13 -0
- package/skills/preset-creator/references/complex-task-skeleton/review.md +107 -0
- package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +111 -0
- package/skills/preset-creator/references/complex-task-skeleton/visual_map.md +50 -0
- package/skills/preset-creator/references/preset-package-skeleton.md +296 -0
- package/templates/AGENTS.md.template +24 -18
- package/templates/dashboard/assets/app-src/00-state.js +13 -0
- package/templates/dashboard/assets/app-src/10-router.js +5 -1
- package/templates/dashboard/assets/app-src/20-overview.js +18 -8
- package/templates/dashboard/assets/app-src/30-tasks.js +92 -246
- package/templates/dashboard/assets/app-src/35-task-detail.js +286 -0
- package/templates/dashboard/assets/app-src/45-review.js +241 -22
- package/templates/dashboard/assets/app-src/50-migration.js +24 -10
- package/templates/dashboard/assets/app-src/55-presets.js +375 -0
- package/templates/dashboard/assets/app-src/60-shared.js +3 -1
- package/templates/dashboard/assets/app-src/90-bindings.js +302 -29
- package/templates/dashboard/assets/app.css +1501 -376
- package/templates/dashboard/assets/app.css.manifest.json +10 -0
- package/templates/dashboard/assets/app.js +1240 -101
- package/templates/dashboard/assets/app.manifest.json +2 -0
- package/templates/dashboard/assets/css-src/00-foundation.css +346 -0
- package/templates/dashboard/assets/css-src/10-panels-flow.css +236 -0
- package/templates/dashboard/assets/css-src/20-briefs-controls.css +398 -0
- package/templates/dashboard/assets/css-src/30-task-index.css +739 -0
- package/templates/dashboard/assets/css-src/35-review-workspace.css +507 -0
- package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +489 -0
- package/templates/dashboard/assets/css-src/45-presets.css +516 -0
- package/templates/dashboard/assets/css-src/50-responsive-overrides.css +551 -0
- package/templates/dashboard/assets/i18n.js +263 -23
- package/templates/ledger/Harness-Ledger.md +13 -25
- package/templates/lessons/lesson-arch-process-change.md +1 -1
- package/templates/lessons/lesson-new-doc.md +1 -1
- package/templates/lessons/lesson-ref-change.md +1 -1
- package/templates/planning/INDEX.md +87 -0
- package/templates/planning/brief.md +1 -1
- package/templates/planning/execution_strategy.md +31 -0
- package/templates/planning/lesson_candidates.md +18 -6
- package/templates/planning/module_session_prompt.md +1 -0
- package/templates/planning/optional/artifacts/INDEX.md +3 -3
- package/templates/planning/optional/references/INDEX.md +3 -3
- package/templates/planning/review.md +41 -0
- package/templates/planning/task_plan.md +5 -21
- package/templates/planning/visual_map.md +13 -9
- package/templates/planning/visual_map.simple.md +52 -0
- package/templates/reference/execution-workflow-standard.md +31 -3
- package/templates/reference/pull-request-standard.md +80 -0
- package/templates/reference/repo-governance-standard.md +7 -6
- package/templates/reference/review-routing-standard.md +6 -0
- package/templates/reference/walkthrough-standard.md +2 -1
- package/templates/verifier/verifier-output.md +1 -1
- package/templates-zh-CN/AGENTS.md.template +25 -19
- package/templates-zh-CN/ledger/Harness-Ledger.md +17 -40
- package/templates-zh-CN/planning/INDEX.md +87 -0
- package/templates-zh-CN/planning/brief.md +1 -1
- package/templates-zh-CN/planning/execution_strategy.md +30 -0
- package/templates-zh-CN/planning/lesson_candidates.md +18 -6
- package/templates-zh-CN/planning/module_session_prompt.md +1 -0
- package/templates-zh-CN/planning/review.md +41 -1
- package/templates-zh-CN/planning/task_plan.md +4 -44
- package/templates-zh-CN/planning/visual_map.md +14 -7
- package/templates-zh-CN/planning/visual_map.simple.md +48 -0
- package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
- package/templates-zh-CN/reference/docs-library-standard.md +1 -1
- package/templates-zh-CN/reference/execution-workflow-standard.md +33 -7
- package/templates-zh-CN/reference/harness-ledger-standard.md +2 -2
- package/templates-zh-CN/reference/pull-request-standard.md +106 -0
- package/templates-zh-CN/reference/repo-governance-standard.md +4 -3
- package/templates-zh-CN/reference/review-routing-standard.md +8 -1
- package/templates-zh-CN/reference/walkthrough-standard.md +3 -2
- package/templates-zh-CN/walkthrough/Closeout-SSoT.md +1 -1
- package/docs-release/assets/dashboard-overview-en.png +0 -0
- package/scripts/smoke-dashboard.mjs +0 -92
- package/scripts/test-harness.mjs +0 -1395
- package/templates/ssot/Feature-SSoT.md +0 -43
- package/templates/ssot/Lessons-SSoT.md +0 -44
- package/templates-zh-CN/ssot/Feature-SSoT.md +0 -49
- package/templates-zh-CN/ssot/Lessons-SSoT.md +0 -49
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readFileSafe, toPosix } from "./core-shared.mjs";
|
|
4
|
+
import { getCell, parseAllMarkdownTables } from "./markdown-utils.mjs";
|
|
5
|
+
|
|
6
|
+
const newRuleCutoff = "2026-05-24";
|
|
7
|
+
|
|
8
|
+
const globalTableSpecs = [
|
|
9
|
+
{ key: "feature-ssot", relativePath: "09-PLANNING/Feature-SSoT.md", allowed: "index-state-route-summary", evaluate: evaluateFeatureRow },
|
|
10
|
+
{ key: "harness-ledger", relativePath: "Harness-Ledger.md", allowed: "task-audit-summary-route", evaluate: evaluateLedgerRow },
|
|
11
|
+
{ key: "closeout-ssot", relativePath: "10-WALKTHROUGH/Closeout-SSoT.md", allowed: "closeout-index-review-route-summary", evaluate: evaluateCloseoutRow },
|
|
12
|
+
{ key: "regression-ssot", relativePath: "05-TEST-QA/Regression-SSoT.md", allowed: "gate-index-current-state", evaluate: evaluateRegressionRow },
|
|
13
|
+
{ key: "cadence-ledger", relativePath: "05-TEST-QA/Cadence-Ledger.md", allowed: "trigger-rule-and-batch-summary", evaluate: evaluateCadenceRow },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export function validateGovernanceTableBoundaries(target) {
|
|
17
|
+
const failures = [];
|
|
18
|
+
const warnings = [];
|
|
19
|
+
for (const spec of globalTableSpecs) {
|
|
20
|
+
const file = path.join(target.docsRoot, spec.relativePath);
|
|
21
|
+
if (!fs.existsSync(file)) continue;
|
|
22
|
+
const relative = toPosix(path.relative(target.projectRoot, file));
|
|
23
|
+
for (const table of parseAllMarkdownTables(readFileSafe(file), relative, spec.key)) {
|
|
24
|
+
for (const row of table.rows) {
|
|
25
|
+
if (isPlaceholderRow(row)) continue;
|
|
26
|
+
for (const finding of spec.evaluate(row)) {
|
|
27
|
+
const rowKey = governanceRowKey(row);
|
|
28
|
+
const message = [
|
|
29
|
+
"governance-table-entropy",
|
|
30
|
+
`${relative}:${table.line}`,
|
|
31
|
+
`${spec.key} row ${rowKey}`,
|
|
32
|
+
finding.reason,
|
|
33
|
+
`allowed=${spec.allowed}`,
|
|
34
|
+
`route=${finding.route}`,
|
|
35
|
+
].join(": ");
|
|
36
|
+
if (isLegacyRow(rowUpdatedDate(row))) warnings.push(`${message}: legacy-report-only`);
|
|
37
|
+
else failures.push(message);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { failures, warnings };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function evaluateFeatureRow(row) {
|
|
46
|
+
const cells = row.cells || {};
|
|
47
|
+
const text = rowText(row);
|
|
48
|
+
const taskPlan = getCell(cells, ["Task Plan", "Task", "任务计划", "路径"], "");
|
|
49
|
+
const evidence = getCell(cells, ["Acceptance Evidence", "Evidence", "验收证据"], "");
|
|
50
|
+
const findings = [];
|
|
51
|
+
if (/09-PLANNING\/MODULES\//i.test(taskPlan) && !isModuleAggregateRow(row) && localDetailPattern().test(text)) {
|
|
52
|
+
findings.push({
|
|
53
|
+
reason: "module-local detail belongs in module_plan.md or task files, not Feature SSoT",
|
|
54
|
+
route: "module-plan-or-task-detail",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (longEvidencePattern().test(evidence) || temporaryPromptPattern().test(text)) {
|
|
58
|
+
findings.push({
|
|
59
|
+
reason: "long evidence or temporary repair prompt belongs in task evidence, not Feature SSoT",
|
|
60
|
+
route: "task-artifacts-or-progress",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return findings;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isModuleAggregateRow(row) {
|
|
67
|
+
const cells = row.cells || {};
|
|
68
|
+
const id = getCell(cells, ["ID"], "");
|
|
69
|
+
const taskPlan = getCell(cells, ["Task Plan", "Task", "任务计划", "路径"], "");
|
|
70
|
+
return /^F-MODULE-/i.test(id) && /09-PLANNING\/MODULES\/[^/]+\/module_plan\.md/i.test(taskPlan);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function evaluateLedgerRow(row) {
|
|
74
|
+
const text = rowText(row);
|
|
75
|
+
const evidence = ledgerEvidenceText(row);
|
|
76
|
+
if (executionLogPattern().test(evidence) || temporaryPromptPattern().test(text) || rawTranscriptPattern().test(evidence)) {
|
|
77
|
+
return [{
|
|
78
|
+
reason: "execution logs, long evidence, and temporary repair prompts belong in task progress/review/artifacts",
|
|
79
|
+
route: "task-progress-review-artifacts",
|
|
80
|
+
}];
|
|
81
|
+
}
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ledgerEvidenceText(row) {
|
|
86
|
+
const cells = row.cells || {};
|
|
87
|
+
return [
|
|
88
|
+
"Evidence Summary",
|
|
89
|
+
"Evidence",
|
|
90
|
+
"Regression Evidence",
|
|
91
|
+
"Review Evidence",
|
|
92
|
+
"Regression",
|
|
93
|
+
"Review",
|
|
94
|
+
"证据摘要",
|
|
95
|
+
"证据",
|
|
96
|
+
"回归",
|
|
97
|
+
"审查",
|
|
98
|
+
].map((column) => getCell(cells, [column], "")).filter(Boolean).join(" ");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function evaluateCloseoutRow(row) {
|
|
102
|
+
const text = rowText(row);
|
|
103
|
+
if (executionLogPattern().test(text) || rawTranscriptPattern().test(text)) {
|
|
104
|
+
return [{
|
|
105
|
+
reason: "closeout rows should route to walkthrough/evidence instead of carrying execution detail",
|
|
106
|
+
route: "walkthrough-or-task-evidence",
|
|
107
|
+
}];
|
|
108
|
+
}
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function evaluateRegressionRow(row) {
|
|
113
|
+
const text = rowText(row);
|
|
114
|
+
if (executionLogPattern().test(text) || temporaryPromptPattern().test(text)) {
|
|
115
|
+
return [{
|
|
116
|
+
reason: "regression global tables should keep gate state and route detailed failure analysis elsewhere",
|
|
117
|
+
route: "regression-detail-or-task-review",
|
|
118
|
+
}];
|
|
119
|
+
}
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function evaluateCadenceRow(row) {
|
|
124
|
+
const text = rowText(row);
|
|
125
|
+
if (rawTranscriptPattern().test(text) || temporaryPromptPattern().test(text)) {
|
|
126
|
+
return [{
|
|
127
|
+
reason: "cadence rows should summarize batch outcomes and route raw run detail elsewhere",
|
|
128
|
+
route: "regression-batch-artifacts",
|
|
129
|
+
}];
|
|
130
|
+
}
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function governanceRowKey(row) {
|
|
135
|
+
return getCell(row.cells || {}, ["ID", "Lesson", "Lesson ID", "Feature", "Work Item", "Gate ID", "Batch ID"], "") || row.id || "unknown-row";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function rowText(row) {
|
|
139
|
+
return Object.values(row.cells || {}).join(" ");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function rowUpdatedDate(row) {
|
|
143
|
+
const value = getCell(row.cells || {}, ["Updated", "Date", "日期", "Last Verified", "最近验证"], "");
|
|
144
|
+
const match = String(value).match(/\d{4}-\d{2}-\d{2}/);
|
|
145
|
+
return match ? match[0] : "";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function isLegacyRow(updated) {
|
|
149
|
+
return updated && updated < newRuleCutoff;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function isPlaceholderRow(row) {
|
|
153
|
+
const text = rowText(row);
|
|
154
|
+
return /\b(?:YYYY|MM|DD|NNN)\b|L-YYYY|HL-YYYY|\[[^\]]+\]|\.\.\.md|\bowner\b|\bShort lesson title\b/i.test(text);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function localDetailPattern() {
|
|
158
|
+
return /\b(module|local|implementation detail|parser branch|button label|copy every|工作项|局部|实现细节)\b/i;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function longEvidencePattern() {
|
|
162
|
+
return /\b(long evidence|full local evidence|raw evidence|stack trace|reviewer transcript|copied raw)\b/i;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function executionLogPattern() {
|
|
166
|
+
return /\b(execution log|command failed|stack trace|raw output|step one|step two|执行流水|命令输出)\b/i;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function temporaryPromptPattern() {
|
|
170
|
+
return /\b(temporary repair prompt|repair prompt|copyable prompt|paste back|临时修复提示)\b/i;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function rawTranscriptPattern() {
|
|
174
|
+
return /\b(raw transcript|reviewer transcript|full transcript|完整记录|原始记录)\b/i;
|
|
175
|
+
}
|
|
@@ -2,9 +2,15 @@ export * from "./core-shared.mjs";
|
|
|
2
2
|
export * from "./markdown-utils.mjs";
|
|
3
3
|
export * from "./capability-registry.mjs";
|
|
4
4
|
export * from "./task-scanner.mjs";
|
|
5
|
+
export * from "./status-builder.mjs";
|
|
5
6
|
export * from "./check-profiles.mjs";
|
|
6
7
|
export * from "./dashboard-data.mjs";
|
|
7
8
|
export * from "./dashboard-workbench.mjs";
|
|
8
9
|
export * from "./migration-planner.mjs";
|
|
10
|
+
export * from "./preset-registry.mjs";
|
|
11
|
+
export * from "./governance-index-generator.mjs";
|
|
9
12
|
export * from "./task-lifecycle.mjs";
|
|
13
|
+
export * from "./task-lesson-sedimentation.mjs";
|
|
10
14
|
export * from "./lesson-maintenance.mjs";
|
|
15
|
+
export * from "./task-index.mjs";
|
|
16
|
+
export * from "./task-tombstone-commands.mjs";
|
|
@@ -12,8 +12,13 @@ import {
|
|
|
12
12
|
collectTasks,
|
|
13
13
|
parseLessonCandidateStatus,
|
|
14
14
|
} from "./task-scanner.mjs";
|
|
15
|
+
import {
|
|
16
|
+
beginGovernanceSync,
|
|
17
|
+
commitGovernanceSync,
|
|
18
|
+
releaseGovernanceSync,
|
|
19
|
+
} from "./governance-sync.mjs";
|
|
15
20
|
|
|
16
|
-
export function promoteLessonCandidate(targetInput, taskId, candidateId, { dryRun = false } = {}) {
|
|
21
|
+
export function promoteLessonCandidate(targetInput, taskId, candidateId, { dryRun = false, apply = false } = {}) {
|
|
17
22
|
const target = normalizeTarget(targetInput);
|
|
18
23
|
const normalizedRef = slug(taskId);
|
|
19
24
|
const matchesBareSlug = (item) => {
|
|
@@ -45,23 +50,43 @@ export function promoteLessonCandidate(targetInput, taskId, candidateId, { dryRu
|
|
|
45
50
|
const title = row.title || lessonId;
|
|
46
51
|
const detailRelative = `docs/01-GOVERNANCE/lessons/${lessonId}-${slug(title)}.md`;
|
|
47
52
|
const detailPath = path.join(target.projectRoot, detailRelative);
|
|
48
|
-
const ssotPath = path.join(target.docsRoot, "01-GOVERNANCE/Lessons-SSoT.md");
|
|
49
|
-
const ssotContent = readFileSafe(ssotPath);
|
|
50
|
-
if (!ssotContent.trim()) throw new Error("Lessons SSoT not found");
|
|
51
53
|
|
|
52
54
|
const changes = [];
|
|
53
55
|
if (!fs.existsSync(detailPath)) changes.push({ action: dryRun ? "would-create" : "create", path: `TARGET:${detailRelative}` });
|
|
54
|
-
if (!ssotContent.includes(lessonId)) changes.push({ action: dryRun ? "would-append" : "append", path: "TARGET:docs/01-GOVERNANCE/Lessons-SSoT.md" });
|
|
55
56
|
if (row.status !== "promoted" || parsed.status !== "promoted") changes.push({ action: dryRun ? "would-update" : "update", path: task.lessonCandidatePath || `TARGET:${toPosix(path.relative(target.projectRoot, candidatePath))}` });
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
const effectiveDryRun = dryRun || !apply;
|
|
59
|
+
if (effectiveDryRun) {
|
|
60
|
+
return {
|
|
61
|
+
dryRun: true,
|
|
62
|
+
applyRequired: true,
|
|
63
|
+
taskId: task.id,
|
|
64
|
+
candidateId: row.id,
|
|
65
|
+
lessonId,
|
|
66
|
+
detailDoc: `TARGET:${detailRelative}`,
|
|
67
|
+
changes: changes.map((change) => ({ ...change, action: change.action.replace(/^(create|append|update)$/, "would-$1") })),
|
|
68
|
+
nextCommand: `harness lesson-promote ${task.shortId} ${row.id} --apply`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
const governanceContext = beginGovernanceSync(target, { operation: `lesson-promote ${task.id} ${row.id}` });
|
|
73
|
+
try {
|
|
74
|
+
fs.mkdirSync(path.dirname(detailPath), { recursive: true });
|
|
75
|
+
if (!fs.existsSync(detailPath)) fs.writeFileSync(detailPath, renderLessonDetail({ lessonId, candidate: row, task, detailRelative }));
|
|
76
|
+
fs.writeFileSync(candidatePath, markCandidatePromoted(candidateContent, row.id, lessonId));
|
|
77
|
+
const commit = commitGovernanceSync(
|
|
78
|
+
governanceContext,
|
|
79
|
+
[
|
|
80
|
+
detailRelative,
|
|
81
|
+
toPosix(path.relative(target.projectRoot, candidatePath)),
|
|
82
|
+
],
|
|
83
|
+
{ message: `chore(harness): promote lesson ${row.id}` },
|
|
84
|
+
);
|
|
63
85
|
|
|
64
|
-
|
|
86
|
+
return { dryRun: false, taskId: task.id, candidateId: row.id, lessonId, detailDoc: `TARGET:${detailRelative}`, changes, governance: { commit } };
|
|
87
|
+
} finally {
|
|
88
|
+
releaseGovernanceSync(governanceContext);
|
|
89
|
+
}
|
|
65
90
|
}
|
|
66
91
|
|
|
67
92
|
function lessonIdFromCandidate(candidateId) {
|
|
@@ -95,24 +120,6 @@ function renderLessonDetail({ lessonId, candidate, task }) {
|
|
|
95
120
|
].join("\n");
|
|
96
121
|
}
|
|
97
122
|
|
|
98
|
-
function appendLessonSsotRow(content, { lessonId, candidate, task, detailRelative }) {
|
|
99
|
-
const lines = String(content || "").split(/\r?\n/);
|
|
100
|
-
const headerIndex = lines.findIndex((line) => /^\|\s*(ID|Lesson ID)\s*\|/.test(line));
|
|
101
|
-
if (headerIndex < 0) throw new Error("Lessons SSoT active table not found");
|
|
102
|
-
const columnCount = splitSimpleRow(lines[headerIndex]).length;
|
|
103
|
-
const date = lessonId.match(/^L-(\d{4}-\d{2}-\d{2})-/)?.[1] || new Date().toISOString().slice(0, 10);
|
|
104
|
-
const detail = `\`${detailRelative}\``;
|
|
105
|
-
const source = `\`${task.path.replace(/^TARGET:/, "docs/").replace(/^docs\/docs\//, "docs/")}/task_plan.md\``;
|
|
106
|
-
const row =
|
|
107
|
-
columnCount === 10
|
|
108
|
-
? `| ${lessonId} | ${escapeCell(candidate.title || lessonId)} | ${source} | process-change | coordinator | candidate | ${escapeCell(candidate.promotionTarget || "governance review")} | ${detail} | ${escapeCell(candidate.id)} | ${date} |`
|
|
109
|
-
: `| ${lessonId} | ${date} | ${source} | process-change | ${escapeCell(candidate.promotionTarget || "governance review")} | ${escapeCell(candidate.title || lessonId)} | ${detail} | pending | ${escapeCell(candidate.id)} |`;
|
|
110
|
-
let insertAt = headerIndex + 1;
|
|
111
|
-
while (insertAt < lines.length && lines[insertAt].trim().startsWith("|")) insertAt += 1;
|
|
112
|
-
lines.splice(insertAt, 0, row);
|
|
113
|
-
return `${lines.join("\n").trimEnd()}\n`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
123
|
function markCandidatePromoted(content, candidateId, lessonId) {
|
|
117
124
|
const lines = String(content || "").split(/\r?\n/);
|
|
118
125
|
const headerIndex = lines.findIndex((line) => /^\|\s*ID\s*\|/.test(line));
|
|
@@ -156,3 +156,36 @@ export function updateMarkdownTableRow(content, headerPattern, updater) {
|
|
|
156
156
|
}
|
|
157
157
|
return { content, matched: false };
|
|
158
158
|
}
|
|
159
|
+
|
|
160
|
+
export function upsertMarkdownTableRow(content, headerPattern, matcher, row) {
|
|
161
|
+
const updated = updateMarkdownTableRow(content, headerPattern, (header, existing) => (matcher(header, existing) ? fitMarkdownTableRow(row, header.length) : null));
|
|
162
|
+
if (updated.matched) return updated.content;
|
|
163
|
+
return appendMarkdownTableRow(content, headerPattern, row);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function appendMarkdownTableRow(content, headerPattern, row) {
|
|
167
|
+
const lines = String(content || "").split(/\r?\n/);
|
|
168
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
169
|
+
if (!lines[index].trim().startsWith("|")) continue;
|
|
170
|
+
const header = splitMarkdownRow(lines[index]);
|
|
171
|
+
if (!header.some((cell) => headerPattern.test(cell))) continue;
|
|
172
|
+
let insertAt = index + 2;
|
|
173
|
+
while (insertAt < lines.length && lines[insertAt].trim().startsWith("|")) insertAt += 1;
|
|
174
|
+
lines.splice(insertAt, 0, `| ${fitMarkdownTableRow(row, header.length).join(" | ")} |`);
|
|
175
|
+
return lines.join("\n");
|
|
176
|
+
}
|
|
177
|
+
return `${String(content || "").trimEnd()}\n\n| ${row.map(markdownTableCell).join(" | ")} |\n`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function fitMarkdownTableRow(row, length) {
|
|
181
|
+
const next = row.map(markdownTableCell);
|
|
182
|
+
while (next.length < length) next.push("");
|
|
183
|
+
return next.slice(0, length);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function markdownTableCell(value) {
|
|
187
|
+
return String(value || "")
|
|
188
|
+
.replace(/\r?\n/g, " ")
|
|
189
|
+
.replaceAll("|", "\\|")
|
|
190
|
+
.trim();
|
|
191
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
normalizeTarget,
|
|
7
7
|
normalizeLocale,
|
|
8
8
|
readFileSafe,
|
|
9
|
+
readJsonSafe,
|
|
9
10
|
existsInDocs,
|
|
10
11
|
walkFiles,
|
|
11
12
|
toPosix,
|
|
@@ -363,12 +364,9 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
|
|
|
363
364
|
}
|
|
364
365
|
const failures = [];
|
|
365
366
|
const warnings = [];
|
|
366
|
-
let
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
} catch (error) {
|
|
370
|
-
return { operation: "migrate-verify", status: "fail", failures: [`invalid session json: ${error.message}`], warnings };
|
|
371
|
-
}
|
|
367
|
+
let readError = null;
|
|
368
|
+
const session = readJsonSafe(sessionPath, null, { onError: (error) => { readError = error; } });
|
|
369
|
+
if (!session) return { operation: "migrate-verify", status: "fail", failures: [`invalid session json: ${readError?.message || "unknown parse error"}`], warnings };
|
|
372
370
|
if (session.operation !== "migrate-run") failures.push("session operation is not migrate-run");
|
|
373
371
|
if (session.schemaVersion !== 1 && session.version !== 1) failures.push("session missing schema version");
|
|
374
372
|
if (session.planOnly) failures.push("plan-only session is not completed migration evidence; rerun migrate-run without --plan-only");
|
|
@@ -22,8 +22,8 @@ export function migrationSampleFiles(target) {
|
|
|
22
22
|
path.join(target.projectRoot, "AGENTS.md"),
|
|
23
23
|
path.join(target.projectRoot, "CLAUDE.md"),
|
|
24
24
|
path.join(target.docsRoot, "Harness-Ledger.md"),
|
|
25
|
-
path.join(target.docsRoot, "09-PLANNING/Feature-SSoT.md"),
|
|
26
25
|
path.join(target.docsRoot, "05-TEST-QA/Regression-SSoT.md"),
|
|
26
|
+
path.join(target.docsRoot, "09-PLANNING/Delivery-SSoT.md"),
|
|
27
27
|
];
|
|
28
28
|
const taskPlans = listTaskPlanPaths(target).slice(0, 20);
|
|
29
29
|
return [...candidates, ...taskPlans].filter((file) => fs.existsSync(file));
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const allowedPhaseKinds = new Set(["init", "execution", "gate"]);
|
|
2
|
+
export const allowedPhaseActors = new Set(["agent", "human", "coordinator"]);
|
|
3
|
+
|
|
4
|
+
export function normalizePhaseKind(value) {
|
|
5
|
+
const normalized = String(value || "")
|
|
6
|
+
.replace(/`/g, "")
|
|
7
|
+
.trim()
|
|
8
|
+
.toLowerCase()
|
|
9
|
+
.replaceAll("_", "-");
|
|
10
|
+
if (!normalized) return "execution";
|
|
11
|
+
if (normalized === "exec" || normalized === "implementation") return "execution";
|
|
12
|
+
if (normalized === "prep" || normalized === "discussion") return "init";
|
|
13
|
+
if (normalized === "review" || normalized === "closeout") return "gate";
|
|
14
|
+
return normalized;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function normalizePhaseActor(value) {
|
|
18
|
+
const normalized = String(value || "")
|
|
19
|
+
.replace(/`/g, "")
|
|
20
|
+
.trim()
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replaceAll("_", "-");
|
|
23
|
+
return normalized || "agent";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function isExecutionPhase(phase) {
|
|
27
|
+
return normalizePhaseKind(phase?.kind) === "execution";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function nonSkippedPhases(phases = []) {
|
|
31
|
+
return phases.filter((phase) => phase.state !== "skipped");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function implementationPhases(phases = []) {
|
|
35
|
+
return nonSkippedPhases(phases).filter(isExecutionPhase);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function phaseCompletionAverage(phases = []) {
|
|
39
|
+
const scored = implementationPhases(phases);
|
|
40
|
+
if (scored.length === 0) return 0;
|
|
41
|
+
return Math.round(scored.reduce((sum, phase) => sum + phase.completion, 0) / scored.length);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function phaseHasRecordedProgress(phase) {
|
|
45
|
+
return (
|
|
46
|
+
phase.completion > 0 ||
|
|
47
|
+
["in_progress", "review", "blocked", "done"].includes(String(phase.state || "").toLowerCase()) ||
|
|
48
|
+
["partial", "present", "waived"].includes(String(phase.evidenceStatus || "").toLowerCase())
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readFileSafe, toPosix } from "./core-shared.mjs";
|
|
4
|
+
|
|
5
|
+
export function validateTaskPresetAuditSnapshot(target, task, presetPackage) {
|
|
6
|
+
const failures = [];
|
|
7
|
+
if (!presetPackage?.audit?.manifestRequired) return failures;
|
|
8
|
+
const bundle = String(task.evidenceBundle || "").replace(/^TARGET:/, "").replace(/^\/+/, "");
|
|
9
|
+
if (!bundle) {
|
|
10
|
+
failures.push(`${task.path} ${task.taskPreset} preset missing Evidence Bundle for manifest audit`);
|
|
11
|
+
return failures;
|
|
12
|
+
}
|
|
13
|
+
const auditPath = path.join(target.projectRoot, bundle, "preset-audit.json");
|
|
14
|
+
if (!fs.existsSync(auditPath)) {
|
|
15
|
+
failures.push(`${task.path} ${task.taskPreset} preset audit missing: TARGET:${toPosix(path.relative(target.projectRoot, auditPath))}`);
|
|
16
|
+
return failures;
|
|
17
|
+
}
|
|
18
|
+
let audit = null;
|
|
19
|
+
try {
|
|
20
|
+
audit = JSON.parse(readFileSafe(auditPath));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
failures.push(`${task.path} ${task.taskPreset} preset audit invalid JSON: ${error.message}`);
|
|
23
|
+
return failures;
|
|
24
|
+
}
|
|
25
|
+
if (audit.preset !== task.taskPreset) {
|
|
26
|
+
failures.push(`${task.path} ${task.taskPreset} preset audit id mismatch: ${audit.preset || "(missing)"}`);
|
|
27
|
+
}
|
|
28
|
+
if (String(audit.version || "") !== String(task.presetVersion || "")) {
|
|
29
|
+
failures.push(`${task.path} ${task.taskPreset} preset audit version mismatch: ${audit.version || "(missing)"}`);
|
|
30
|
+
}
|
|
31
|
+
if (!audit.manifestSha256) {
|
|
32
|
+
failures.push(`${task.path} ${task.taskPreset} preset audit missing manifestSha256`);
|
|
33
|
+
} else if (audit.manifestSha256 !== presetPackage.manifestSha256) {
|
|
34
|
+
failures.push(`${task.path} ${task.taskPreset} preset manifest hash mismatch: task audit ${audit.manifestSha256}, current ${presetPackage.manifestSha256}`);
|
|
35
|
+
}
|
|
36
|
+
return failures;
|
|
37
|
+
}
|