coding-agent-harness 1.0.4 → 1.0.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.
- package/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +2 -2
- package/LICENSE +661 -21
- package/LICENSE-EXCEPTION.md +37 -0
- package/README.md +96 -4
- package/README.zh-CN.md +75 -4
- package/SKILL.md +52 -51
- package/dist/build-dist.mjs +189 -0
- package/dist/check-dist-observation.mjs +428 -0
- package/dist/check-harness.mjs +489 -0
- package/dist/check-import-graph.mjs +511 -0
- package/dist/check-runtime-emit.mjs +304 -0
- package/dist/check-type-boundaries.mjs +139 -0
- package/dist/commands/dashboard-command.mjs +80 -0
- package/dist/commands/migration-command.mjs +152 -0
- package/dist/commands/preset-command.mjs +91 -0
- package/dist/commands/task-command.mjs +324 -0
- package/dist/harness.mjs +304 -0
- package/dist/lib/capability-registry.mjs +643 -0
- package/dist/lib/check-module-parallel.mjs +227 -0
- package/dist/lib/check-profiles.mjs +414 -0
- package/dist/lib/check-task-contracts.mjs +54 -0
- package/dist/lib/core-shared.mjs +254 -0
- package/dist/lib/dashboard-data.mjs +608 -0
- package/dist/lib/dashboard-workbench.mjs +334 -0
- package/dist/lib/dashboard-writer.mjs +200 -0
- package/dist/lib/git-status-summary.mjs +45 -0
- package/dist/lib/governance-index-generator.mjs +236 -0
- package/dist/lib/governance-sync.mjs +617 -0
- package/dist/lib/governance-table-boundary.mjs +161 -0
- package/{scripts → dist}/lib/harness-core.mjs +3 -0
- package/dist/lib/harness-paths.mjs +338 -0
- package/dist/lib/lesson-maintenance.mjs +139 -0
- package/dist/lib/markdown-utils.mjs +193 -0
- package/dist/lib/migration-planner.mjs +439 -0
- package/dist/lib/migration-support.mjs +317 -0
- package/dist/lib/phase-kind.mjs +46 -0
- package/dist/lib/preset-audit-contracts.mjs +40 -0
- package/dist/lib/preset-engine.mjs +516 -0
- package/dist/lib/preset-registry.mjs +831 -0
- package/dist/lib/preset-resource-contracts.mjs +83 -0
- package/dist/lib/review-confirm-git-gate.mjs +244 -0
- package/dist/lib/status-builder.mjs +87 -0
- package/{scripts → dist}/lib/status-dashboard-renderer.mjs +48 -47
- package/dist/lib/structure-migration.mjs +404 -0
- package/dist/lib/subagent-authorization-audit.mjs +198 -0
- package/dist/lib/task-audit-metadata.mjs +376 -0
- package/dist/lib/task-audit-migration.mjs +355 -0
- package/dist/lib/task-completion-consistency.mjs +29 -0
- package/dist/lib/task-index.mjs +133 -0
- package/dist/lib/task-lesson-candidates.mjs +239 -0
- package/dist/lib/task-lesson-sedimentation.mjs +300 -0
- package/dist/lib/task-lifecycle/create-task-helpers.mjs +84 -0
- package/dist/lib/task-lifecycle/phase-sync.mjs +82 -0
- package/dist/lib/task-lifecycle/review-confirm.mjs +93 -0
- package/dist/lib/task-lifecycle/review-gates.mjs +62 -0
- package/dist/lib/task-lifecycle/review-submission.mjs +52 -0
- package/dist/lib/task-lifecycle/scaffold-provenance.mjs +54 -0
- package/dist/lib/task-lifecycle/template-files.mjs +52 -0
- package/dist/lib/task-lifecycle/text-utils.mjs +26 -0
- package/dist/lib/task-lifecycle.mjs +611 -0
- package/dist/lib/task-metadata.mjs +116 -0
- package/dist/lib/task-review-model.mjs +474 -0
- package/dist/lib/task-scanner.mjs +439 -0
- package/dist/lib/task-tombstone-commands.mjs +125 -0
- package/dist/postinstall.mjs +14 -0
- package/dist/run-built-tests.mjs +84 -0
- package/docs-release/README.md +1 -0
- package/docs-release/architecture/overview.md +13 -13
- package/docs-release/architecture/overview.zh-CN.md +13 -13
- package/docs-release/architecture/system-explainer/01-system-overview.md +218 -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 +241 -0
- package/docs-release/architecture/system-explainer/05-data-flow.md +276 -0
- package/docs-release/architecture/system-explainer/06-preset-and-migration.md +300 -0
- package/docs-release/architecture/system-explainer/README.md +67 -0
- package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +227 -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 +252 -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 +320 -0
- package/docs-release/architecture/system-explainer/en-US/README.md +70 -0
- package/docs-release/guides/agent-installation.en-US.md +22 -15
- package/docs-release/guides/agent-installation.md +23 -15
- package/docs-release/guides/contributing.md +3 -3
- package/docs-release/guides/contributing.zh-CN.md +3 -3
- package/docs-release/guides/document-audience-and-surfaces.en-US.md +10 -10
- package/docs-release/guides/document-audience-and-surfaces.md +10 -10
- package/docs-release/guides/legacy-migration-agent-prompt.md +25 -2
- package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +25 -2
- package/docs-release/guides/migration-playbook.en-US.md +63 -1
- package/docs-release/guides/migration-playbook.md +59 -1
- package/docs-release/guides/parent-control-repository-pattern.en-US.md +25 -25
- package/docs-release/guides/parent-control-repository-pattern.md +25 -25
- package/docs-release/guides/preset-development.md +28 -4
- package/docs-release/guides/repository-operating-models.en-US.md +21 -21
- package/docs-release/guides/repository-operating-models.md +21 -21
- package/docs-release/guides/task-state-machine.en-US.md +35 -18
- package/docs-release/guides/task-state-machine.md +35 -18
- package/docs-release/guides/typescript-runtime-migration-closeout.md +96 -0
- package/examples/minimal-project/AGENTS.md +2 -2
- package/examples/minimal-project/coding-agent-harness/harness.yaml +14 -0
- package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/INDEX.md +60 -0
- package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/progress.md +11 -0
- package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/review.md +1 -1
- package/package.json +22 -13
- package/presets/legacy-migration/preset.yaml +5 -5
- package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
- package/presets/lesson-sedimentation/preset.yaml +3 -3
- package/presets/module/preset.yaml +2 -2
- package/presets/module/templates/execution_strategy.append.md +1 -1
- package/presets/module/templates/task_plan.append.md +3 -3
- package/presets/standard-task/preset.yaml +2 -2
- package/references/adversarial-review-standard.md +2 -2
- package/references/agents-md-pattern.md +14 -14
- package/references/cadence-ledger.md +1 -1
- package/references/ci-cd-standard.md +1 -1
- package/references/delivery-operating-model-standard.md +4 -4
- package/references/docs-directory-standard.md +65 -159
- package/references/external-source-intake-standard.md +10 -10
- package/references/harness-ledger.md +6 -6
- package/references/legacy-12-phase-bootstrap.md +2 -2
- package/references/lessons-governance.md +15 -15
- package/references/long-running-task-standard.md +6 -6
- package/references/module-parallel-standard.md +34 -34
- package/references/planning-loop.md +6 -6
- package/references/project-onboarding-audit.md +4 -4
- package/references/regression-system.md +2 -2
- package/references/repo-governance-standard.md +4 -4
- package/references/review-routing-standard.md +1 -1
- package/references/ssot-governance.md +19 -19
- package/references/taskr-gap-analysis.md +5 -5
- package/references/walkthrough-closeout.md +14 -14
- package/references/worktree-parallel.md +3 -3
- package/skills/preset-creator/references/complex-task-skeleton/brief.md +11 -0
- package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +1 -1
- package/skills/preset-creator/references/preset-package-skeleton.md +5 -5
- package/templates/AGENTS.md.template +31 -29
- package/templates/architecture/README.md +4 -4
- package/templates/architecture/service-catalog.md +2 -2
- package/templates/architecture/services/service-template.md +1 -1
- package/templates/dashboard/assets/app-src/00-state.js +12 -0
- package/templates/dashboard/assets/app-src/10-router.js +3 -0
- package/templates/dashboard/assets/app-src/20-overview.js +13 -3
- package/templates/dashboard/assets/app-src/35-task-detail.js +46 -6
- package/templates/dashboard/assets/app-src/40-modules.js +1 -1
- 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 +131 -0
- package/templates/dashboard/assets/app.css +583 -0
- package/templates/dashboard/assets/app.css.manifest.json +1 -0
- package/templates/dashboard/assets/app.js +585 -11
- package/templates/dashboard/assets/app.manifest.json +1 -0
- package/templates/dashboard/assets/css-src/00-foundation.css +4 -0
- package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +62 -0
- package/templates/dashboard/assets/css-src/45-presets.css +516 -0
- package/templates/dashboard/assets/i18n.js +144 -4
- package/templates/development/README.md +10 -10
- package/templates/development/cross-repo-debugging.md +3 -3
- package/templates/development/external-context/service-template.md +2 -2
- package/templates/development/external-source-packs/README.md +4 -4
- package/templates/integrations/README.md +4 -4
- package/templates/integrations/api-contract.md +2 -2
- package/templates/integrations/event-contract.md +2 -2
- package/templates/integrations/third-party/vendor-template.md +2 -2
- package/templates/integrations/webhook-contract.md +2 -2
- package/templates/ledger/Harness-Ledger.md +1 -1
- package/templates/planning/INDEX.md +88 -0
- package/templates/planning/brief.md +1 -1
- package/templates/planning/module_session_prompt.md +2 -1
- package/templates/planning/review.md +0 -18
- package/templates/planning/task_plan.md +5 -44
- package/templates/planning/visual_map.md +13 -9
- package/templates/planning/visual_map.simple.md +52 -0
- package/templates/planning/walkthrough.md +47 -0
- package/templates/reference/docs-library-standard.md +8 -8
- package/templates/reference/execution-workflow-standard.md +29 -2
- package/templates/reference/external-source-intake-standard.md +15 -15
- package/templates/reference/repo-governance-standard.md +1 -1
- package/templates/ssot/Module-Registry.md +1 -1
- package/templates/walkthrough/walkthrough-template.md +2 -2
- package/templates-zh-CN/AGENTS.md.template +31 -29
- package/templates-zh-CN/CLAUDE.md.template +1 -1
- package/templates-zh-CN/architecture/README.md +4 -4
- package/templates-zh-CN/architecture/service-catalog.md +2 -2
- package/templates-zh-CN/architecture/services/service-template.md +1 -1
- package/templates-zh-CN/development/README.md +10 -10
- package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
- package/templates-zh-CN/development/external-context/service-template.md +2 -2
- package/templates-zh-CN/development/external-source-packs/README.md +4 -4
- package/templates-zh-CN/integrations/README.md +4 -4
- package/templates-zh-CN/integrations/api-contract.md +2 -2
- package/templates-zh-CN/integrations/event-contract.md +2 -2
- package/templates-zh-CN/integrations/third-party/vendor-template.md +2 -2
- package/templates-zh-CN/integrations/webhook-contract.md +2 -2
- package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
- package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
- package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
- package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
- package/templates-zh-CN/planning/INDEX.md +87 -0
- package/templates-zh-CN/planning/brief.md +1 -1
- package/templates-zh-CN/planning/module_session_prompt.md +12 -11
- package/templates-zh-CN/planning/review.md +0 -18
- package/templates-zh-CN/planning/task_plan.md +3 -63
- 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/planning/walkthrough.md +47 -0
- package/templates-zh-CN/reference/adversarial-review-standard.md +2 -2
- package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
- package/templates-zh-CN/reference/docs-library-standard.md +28 -28
- package/templates-zh-CN/reference/execution-workflow-standard.md +32 -7
- package/templates-zh-CN/reference/external-source-intake-standard.md +16 -16
- package/templates-zh-CN/reference/harness-ledger-standard.md +6 -6
- package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
- package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
- package/templates-zh-CN/reference/review-routing-standard.md +1 -1
- package/templates-zh-CN/reference/walkthrough-standard.md +7 -7
- package/templates-zh-CN/reference/worktree-standard.md +1 -1
- package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
- package/templates-zh-CN/ssot/Delivery-SSoT.md +3 -3
- package/templates-zh-CN/ssot/Module-Registry.md +3 -3
- package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
- package/templates-zh-CN/walkthrough/walkthrough-template.md +5 -5
- package/tsconfig.dist.json +16 -0
- package/tsconfig.json +25 -0
- package/tsconfig.runtime.json +24 -0
- package/examples/minimal-project/.harness-capabilities.json +0 -8
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +0 -11
- package/scripts/check-harness.mjs +0 -508
- package/scripts/commands/dashboard-command.mjs +0 -67
- package/scripts/commands/migration-command.mjs +0 -96
- package/scripts/commands/preset-command.mjs +0 -73
- package/scripts/commands/task-command.mjs +0 -327
- package/scripts/harness.mjs +0 -287
- package/scripts/lib/capability-registry.mjs +0 -591
- package/scripts/lib/check-module-parallel.mjs +0 -237
- package/scripts/lib/check-profiles.mjs +0 -418
- package/scripts/lib/check-task-contracts.mjs +0 -47
- package/scripts/lib/core-shared.mjs +0 -196
- package/scripts/lib/dashboard-data.mjs +0 -412
- package/scripts/lib/dashboard-workbench.mjs +0 -257
- package/scripts/lib/dashboard-writer.mjs +0 -198
- package/scripts/lib/git-status-summary.mjs +0 -46
- package/scripts/lib/governance-index-generator.mjs +0 -174
- package/scripts/lib/governance-sync.mjs +0 -514
- package/scripts/lib/governance-table-boundary.mjs +0 -175
- package/scripts/lib/lesson-maintenance.mjs +0 -152
- package/scripts/lib/markdown-utils.mjs +0 -158
- package/scripts/lib/migration-planner.mjs +0 -478
- package/scripts/lib/migration-support.mjs +0 -312
- package/scripts/lib/preset-audit-contracts.mjs +0 -37
- package/scripts/lib/preset-engine.mjs +0 -497
- package/scripts/lib/preset-registry.mjs +0 -627
- package/scripts/lib/preset-resource-contracts.mjs +0 -83
- package/scripts/lib/review-confirm-git-gate.mjs +0 -248
- package/scripts/lib/subagent-authorization-audit.mjs +0 -196
- package/scripts/lib/task-completion-consistency.mjs +0 -16
- package/scripts/lib/task-index.mjs +0 -93
- package/scripts/lib/task-lesson-candidates.mjs +0 -242
- package/scripts/lib/task-lesson-sedimentation.mjs +0 -326
- package/scripts/lib/task-lifecycle/review-confirm.mjs +0 -101
- package/scripts/lib/task-lifecycle/review-gates.mjs +0 -70
- package/scripts/lib/task-lifecycle/text-utils.mjs +0 -24
- package/scripts/lib/task-lifecycle.mjs +0 -649
- package/scripts/lib/task-review-model.mjs +0 -469
- package/scripts/lib/task-scanner.mjs +0 -576
- package/scripts/lib/task-tombstone-commands.mjs +0 -140
- package/scripts/postinstall.mjs +0 -14
- package/templates/walkthrough/Closeout-SSoT.md +0 -43
- package/templates-zh-CN/walkthrough/Closeout-SSoT.md +0 -42
- /package/examples/minimal-project/{docs → coding-agent-harness/governance/generated}/Harness-Ledger.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/brief.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/execution_strategy.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/findings.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/lesson_candidates.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/task_plan.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/visual_map.md +0 -0
|
@@ -1,649 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import crypto from "node:crypto";
|
|
4
|
-
import {
|
|
5
|
-
visualMapFile,
|
|
6
|
-
legacyVisualRoadmapFile,
|
|
7
|
-
lessonCandidatesFile,
|
|
8
|
-
longRunningTaskContractFile,
|
|
9
|
-
allowedTaskStates,
|
|
10
|
-
allowedTaskBudgets,
|
|
11
|
-
allowedPhaseStates,
|
|
12
|
-
allowedEvidenceStatus,
|
|
13
|
-
normalizeTarget,
|
|
14
|
-
normalizeLocale,
|
|
15
|
-
toPosix,
|
|
16
|
-
readFileSafe,
|
|
17
|
-
readBundledTemplate,
|
|
18
|
-
localizedTemplateSource,
|
|
19
|
-
todayDate,
|
|
20
|
-
localDate,
|
|
21
|
-
datePrefix,
|
|
22
|
-
nowTimestamp,
|
|
23
|
-
normalizeTaskId,
|
|
24
|
-
renderTaskTemplate,
|
|
25
|
-
} from "./core-shared.mjs";
|
|
26
|
-
import { readCapabilityRegistry } from "./capability-registry.mjs";
|
|
27
|
-
import { readPresetPackage } from "./preset-registry.mjs";
|
|
28
|
-
import {
|
|
29
|
-
assertPresetWriteScope,
|
|
30
|
-
buildPresetContext,
|
|
31
|
-
evaluateTemplateValues,
|
|
32
|
-
resolvePresetInputs,
|
|
33
|
-
renderPresetResourceIndex,
|
|
34
|
-
renderPresetTaskTemplate,
|
|
35
|
-
} from "./preset-engine.mjs";
|
|
36
|
-
import {
|
|
37
|
-
collectTasks,
|
|
38
|
-
collectReviewRisks,
|
|
39
|
-
isBlockingReviewRisk,
|
|
40
|
-
listTaskPlanPaths,
|
|
41
|
-
parseTaskBudget,
|
|
42
|
-
taskIdForDirectory,
|
|
43
|
-
taskScannerVersion,
|
|
44
|
-
} from "./task-scanner.mjs";
|
|
45
|
-
import {
|
|
46
|
-
getColumn,
|
|
47
|
-
firstColumn,
|
|
48
|
-
updateMarkdownTableRow,
|
|
49
|
-
} from "./markdown-utils.mjs";
|
|
50
|
-
import {
|
|
51
|
-
validateLifecycleTransition,
|
|
52
|
-
validateReviewEntryGate,
|
|
53
|
-
} from "./task-lifecycle/review-gates.mjs";
|
|
54
|
-
import { confirmTaskReview as confirmTaskReviewWithContext } from "./task-lifecycle/review-confirm.mjs";
|
|
55
|
-
import { appendProgressLog, markdownCell } from "./task-lifecycle/text-utils.mjs";
|
|
56
|
-
import {
|
|
57
|
-
beginGovernanceSync,
|
|
58
|
-
commitGovernanceSync,
|
|
59
|
-
governanceRelativePaths,
|
|
60
|
-
releaseGovernanceSync,
|
|
61
|
-
syncModuleStepGovernance,
|
|
62
|
-
syncTaskGovernance,
|
|
63
|
-
} from "./governance-sync.mjs";
|
|
64
|
-
|
|
65
|
-
function taskTemplateFiles({ locale = "en-US" } = {}) {
|
|
66
|
-
return [
|
|
67
|
-
["brief.md", "templates/planning/brief.md"],
|
|
68
|
-
["task_plan.md", "templates/planning/task_plan.md"],
|
|
69
|
-
["execution_strategy.md", "templates/planning/execution_strategy.md"],
|
|
70
|
-
[visualMapFile, "templates/planning/visual_map.md"],
|
|
71
|
-
["findings.md", "templates/planning/findings.md"],
|
|
72
|
-
[lessonCandidatesFile, "templates/planning/lesson_candidates.md"],
|
|
73
|
-
["progress.md", "templates/planning/progress.md"],
|
|
74
|
-
["review.md", "templates/planning/review.md"],
|
|
75
|
-
].map(([destination, source]) => [destination, localizedTemplateSource(source, locale)]);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function simpleTaskTemplateFiles({ locale = "en-US" } = {}) {
|
|
79
|
-
return [
|
|
80
|
-
["brief.md", "templates/planning/brief.md"],
|
|
81
|
-
["task_plan.md", "templates/planning/task_plan.md"],
|
|
82
|
-
[visualMapFile, "templates/planning/visual_map.md"],
|
|
83
|
-
["progress.md", "templates/planning/progress.md"],
|
|
84
|
-
].map(([destination, source]) => [destination, localizedTemplateSource(source, locale)]);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function optionalTaskTemplateFiles({ locale = "en-US" } = {}) {
|
|
88
|
-
return [
|
|
89
|
-
["references/INDEX.md", "templates/planning/optional/references/INDEX.md"],
|
|
90
|
-
["artifacts/INDEX.md", "templates/planning/optional/artifacts/INDEX.md"],
|
|
91
|
-
].map(([destination, source]) => [destination, localizedTemplateSource(source, locale)]);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function moduleTemplateFiles({ locale = "en-US" } = {}) {
|
|
95
|
-
return [
|
|
96
|
-
["brief.md", "templates/planning/module_brief.md"],
|
|
97
|
-
["module_plan.md", "templates/planning/module_plan.md"],
|
|
98
|
-
["execution_strategy.md", "templates/planning/execution_strategy.md"],
|
|
99
|
-
[visualMapFile, "templates/planning/visual_map.md"],
|
|
100
|
-
["session_prompt.md", "templates/planning/module_session_prompt.md"],
|
|
101
|
-
].map(([destination, source]) => [destination, localizedTemplateSource(source, locale)]);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function taskRoot(target, taskId, { moduleKey = "" } = {}) {
|
|
105
|
-
const normalizedTaskId = normalizeTaskId(taskId);
|
|
106
|
-
if (moduleKey) return path.join(target.docsRoot, "09-PLANNING/MODULES", normalizeTaskId(moduleKey), normalizedTaskId);
|
|
107
|
-
return path.join(target.docsRoot, "09-PLANNING/TASKS", normalizedTaskId);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function resolveTaskDirectory(target, taskRef) {
|
|
111
|
-
const raw = String(taskRef || "").replace(/^docs\/09-PLANNING\//, "").replace(/^\/+/, "");
|
|
112
|
-
if (!raw) throw new Error("Missing task id");
|
|
113
|
-
const direct = raw.startsWith("TASKS/") || raw.startsWith("MODULES/") ? path.join(target.docsRoot, "09-PLANNING", raw) : "";
|
|
114
|
-
if (direct && fs.existsSync(path.join(direct, "task_plan.md"))) return direct;
|
|
115
|
-
const normalized = normalizeTaskId(raw);
|
|
116
|
-
const candidates = listTaskPlanPaths(target)
|
|
117
|
-
.map((taskPlanPath) => path.dirname(taskPlanPath))
|
|
118
|
-
.filter((taskDir) => {
|
|
119
|
-
const id = taskIdForDirectory(target, taskDir);
|
|
120
|
-
const dirName = path.basename(taskDir);
|
|
121
|
-
return id === raw || id.endsWith(`/${raw}`) || dirName === normalized;
|
|
122
|
-
});
|
|
123
|
-
if (candidates.length === 1) return candidates[0];
|
|
124
|
-
if (candidates.length > 1) {
|
|
125
|
-
const options = candidates.map((taskDir) => `- ${taskIdForDirectory(target, taskDir)}`).join("\n");
|
|
126
|
-
throw new Error(`Ambiguous task reference: ${taskRef}\n${options}`);
|
|
127
|
-
}
|
|
128
|
-
// Try bare slug resolution: match normalized slug against dated directories
|
|
129
|
-
if (!datePrefix.test(normalized)) {
|
|
130
|
-
const datedCandidates = listTaskPlanPaths(target)
|
|
131
|
-
.map((taskPlanPath) => path.dirname(taskPlanPath))
|
|
132
|
-
.filter((taskDir) => {
|
|
133
|
-
const dirName = path.basename(taskDir);
|
|
134
|
-
return datePrefix.test(dirName) && dirName.replace(datePrefix, "") === normalized;
|
|
135
|
-
});
|
|
136
|
-
if (datedCandidates.length === 1) return datedCandidates[0];
|
|
137
|
-
if (datedCandidates.length > 1) {
|
|
138
|
-
const options = datedCandidates.map((taskDir) => `- ${taskIdForDirectory(target, taskDir)}`).join("\n");
|
|
139
|
-
throw new Error(`Ambiguous task reference: ${taskRef}\n${options}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
const legacy = taskRoot(target, normalized);
|
|
143
|
-
if (fs.existsSync(path.join(legacy, "task_plan.md"))) return legacy;
|
|
144
|
-
throw new Error(`Task not found: ${taskRef}`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function findTaskByDirectory(target, taskDir) {
|
|
148
|
-
const id = taskIdForDirectory(target, taskDir);
|
|
149
|
-
return collectTasks(target).find((task) => task.id === id) || null;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function stateLabel(state, locale) {
|
|
153
|
-
if (normalizeLocale(locale) !== "zh-CN") return state;
|
|
154
|
-
return (
|
|
155
|
-
{
|
|
156
|
-
not_started: "未开始",
|
|
157
|
-
planned: "未开始",
|
|
158
|
-
in_progress: "进行中",
|
|
159
|
-
review: "审查中",
|
|
160
|
-
blocked: "已阻塞",
|
|
161
|
-
done: "已完成",
|
|
162
|
-
}[state] || state
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function normalizeTaskBudgetInput(budget) {
|
|
167
|
-
const normalized = String(budget || "standard").trim().toLowerCase().replaceAll("_", "-");
|
|
168
|
-
if (allowedTaskBudgets.has(normalized)) return normalized;
|
|
169
|
-
throw new Error(`Invalid task budget: ${budget}. Expected one of: simple, standard, complex`);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function normalizeTaskPresetInput(preset, { targetInput = "" } = {}) {
|
|
173
|
-
const normalized = String(preset || "none").trim().toLowerCase().replaceAll("_", "-");
|
|
174
|
-
if (!normalized || normalized === "none") return "none";
|
|
175
|
-
return readPresetPackage(normalized, { targetInput }).id;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function taskFilesForBudget({ budget, locale }) {
|
|
179
|
-
if (budget === "simple") return simpleTaskTemplateFiles({ locale });
|
|
180
|
-
if (budget === "complex") return [...taskTemplateFiles({ locale }), ...optionalTaskTemplateFiles({ locale })];
|
|
181
|
-
return taskTemplateFiles({ locale });
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function appendLongRunningContractFile(files, { locale, longRunning }) {
|
|
185
|
-
if (!longRunning) return files;
|
|
186
|
-
return [...files, [longRunningTaskContractFile, localizedTemplateSource("templates/planning/long-running-task-contract.md", locale)]];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function updateProgressState(content, state, locale) {
|
|
190
|
-
const label = stateLabel(state, locale);
|
|
191
|
-
if (/^##\s*状态[::][^\n]*/im.test(content)) {
|
|
192
|
-
return content.replace(/^##\s*状态[::][^\n]*/im, `## 状态:${label}`);
|
|
193
|
-
}
|
|
194
|
-
if (/^##\s*(?:Current Status|Status)\s*\n+\s*[^\n]+/im.test(content)) {
|
|
195
|
-
return content.replace(/^##\s*(Current Status|Status)\s*\n+\s*[^\n]+/im, `## $1\n\n${label}`);
|
|
196
|
-
}
|
|
197
|
-
return `${content.trimEnd()}\n\n## Status\n\n${label}\n`;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function ensureDatePrefix(slug) {
|
|
201
|
-
if (datePrefix.test(slug)) return slug;
|
|
202
|
-
return `${localDate()}-${slug}`;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function bareSlug(datedId) {
|
|
206
|
-
if (datePrefix.test(datedId)) return datedId.replace(datePrefix, "");
|
|
207
|
-
return datedId;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function createTask(targetInput, taskId, { title = "", locale = "en-US", dryRun = false, moduleKey = "", budget = "standard", longRunning = false, preset = "", fromSession = "", presetArgs = [] } = {}) {
|
|
211
|
-
const requestedPreset = preset || (moduleKey ? "module" : "");
|
|
212
|
-
const normalizedPreset = normalizeTaskPresetInput(requestedPreset, { targetInput });
|
|
213
|
-
const presetPackage = normalizedPreset === "none" ? null : readPresetPackage(normalizedPreset, { targetInput });
|
|
214
|
-
const presetInputs = presetPackage ? resolvePresetInputs(presetPackage, { cliArgs: presetArgs, fromSession, targetInput }) : null;
|
|
215
|
-
const target = normalizeTarget(presetInputs?.targetInput || targetInput);
|
|
216
|
-
if (presetInputs?.targetInput && targetInput && targetInput !== "." && path.resolve(targetInput) !== path.resolve(presetInputs.targetInput)) {
|
|
217
|
-
throw new Error(`--from-session target mismatch: session target is ${presetInputs.targetInput}`);
|
|
218
|
-
}
|
|
219
|
-
const normalizedBudget = normalizeTaskBudgetInput(budget);
|
|
220
|
-
if (presetPackage && !presetPackage.compatibleBudgets.includes(normalizedBudget)) throw new Error(`${normalizedPreset} preset requires --budget ${presetPackage.compatibleBudgets.join("|")}`);
|
|
221
|
-
if (presetPackage?.task?.projectLevelOnly === true && moduleKey) throw new Error(`${normalizedPreset} preset is project-level and cannot be combined with --module`);
|
|
222
|
-
if (presetPackage?.task?.requiresFromSession === true && !fromSession) throw new Error(`${normalizedPreset} preset requires --from-session`);
|
|
223
|
-
const rawNormalized = normalizeTaskId(taskId || (presetPackage?.task?.defaultTaskId || ""));
|
|
224
|
-
const normalizedTaskId = ensureDatePrefix(rawNormalized);
|
|
225
|
-
if (!normalizedTaskId) throw new Error("Missing task id");
|
|
226
|
-
const semanticSlug = bareSlug(normalizedTaskId);
|
|
227
|
-
const normalizedModuleKey = moduleKey ? normalizeTaskId(moduleKey) : "";
|
|
228
|
-
const normalizedLocale = normalizeLocale(locale || readCapabilityRegistry(target).locale);
|
|
229
|
-
const taskTitle = title || (normalizedPreset === "legacy-migration" ? "Harness v1 legacy migration" : semanticSlug);
|
|
230
|
-
const directory = taskRoot(target, normalizedTaskId, { moduleKey: normalizedModuleKey });
|
|
231
|
-
if (fs.existsSync(directory)) throw new Error(`Task already exists: ${normalizedTaskId}`);
|
|
232
|
-
const evaluatedPresetValues = presetPackage ? evaluateTemplateValues(presetPackage, presetInputs.inputs, { taskId: normalizedTaskId, taskTitle, moduleKey: normalizedModuleKey }) : null;
|
|
233
|
-
const presetContext = presetPackage
|
|
234
|
-
? buildPresetContext({ ...presetPackage, task: { ...(presetPackage.task || {}), kind: presetPackage.task?.kind || "general" } }, {
|
|
235
|
-
target,
|
|
236
|
-
taskDir: directory,
|
|
237
|
-
taskId: normalizedTaskId,
|
|
238
|
-
taskTitle,
|
|
239
|
-
resolvedInputs: presetInputs.inputs,
|
|
240
|
-
evaluatedValues: evaluatedPresetValues,
|
|
241
|
-
})
|
|
242
|
-
: null;
|
|
243
|
-
const changes = [];
|
|
244
|
-
const governanceContext = beginGovernanceSync(target, { operation: `new-task ${normalizedTaskId}`, dryRun });
|
|
245
|
-
try {
|
|
246
|
-
if (normalizedModuleKey) {
|
|
247
|
-
const moduleDirectory = path.dirname(directory);
|
|
248
|
-
for (const [destination, source] of moduleTemplateFiles({ locale: normalizedLocale })) {
|
|
249
|
-
const destinationPath = path.join(moduleDirectory, destination);
|
|
250
|
-
if (fs.existsSync(destinationPath)) continue;
|
|
251
|
-
changes.push({
|
|
252
|
-
destination: toPosix(path.relative(target.projectRoot, destinationPath)),
|
|
253
|
-
source,
|
|
254
|
-
action: dryRun ? "would-create" : "create",
|
|
255
|
-
});
|
|
256
|
-
if (presetPackage) assertPresetWriteScope(presetPackage, toPosix(path.relative(target.projectRoot, destinationPath)));
|
|
257
|
-
if (dryRun) continue;
|
|
258
|
-
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
259
|
-
fs.writeFileSync(
|
|
260
|
-
destinationPath,
|
|
261
|
-
renderTaskTemplate(readBundledTemplate(source), {
|
|
262
|
-
taskId: normalizedModuleKey,
|
|
263
|
-
title: normalizedModuleKey,
|
|
264
|
-
locale: normalizedLocale,
|
|
265
|
-
budget: normalizedBudget,
|
|
266
|
-
}),
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
const files = appendLongRunningContractFile(taskFilesForBudget({ budget: normalizedBudget, locale: normalizedLocale }), {
|
|
271
|
-
locale: normalizedLocale,
|
|
272
|
-
longRunning,
|
|
273
|
-
});
|
|
274
|
-
for (const [destination, source] of files) {
|
|
275
|
-
const destinationPath = path.join(directory, destination);
|
|
276
|
-
changes.push({
|
|
277
|
-
destination: toPosix(path.relative(target.projectRoot, destinationPath)),
|
|
278
|
-
source,
|
|
279
|
-
action: dryRun ? "would-create" : "create",
|
|
280
|
-
});
|
|
281
|
-
if (presetPackage) assertPresetWriteScope(presetPackage, toPosix(path.relative(target.projectRoot, destinationPath)));
|
|
282
|
-
if (dryRun) continue;
|
|
283
|
-
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
284
|
-
fs.writeFileSync(
|
|
285
|
-
destinationPath,
|
|
286
|
-
renderPresetTaskTemplate(destination, renderTaskTemplate(readBundledTemplate(source), {
|
|
287
|
-
taskId: normalizedTaskId,
|
|
288
|
-
title: taskTitle,
|
|
289
|
-
locale: normalizedLocale,
|
|
290
|
-
budget: normalizedBudget,
|
|
291
|
-
}), presetContext),
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
if (presetContext) {
|
|
295
|
-
for (const evidence of presetContext.evidenceFiles) {
|
|
296
|
-
const destinationPath = path.join(target.projectRoot, evidence.relativePath);
|
|
297
|
-
changes.push({
|
|
298
|
-
destination: toPosix(evidence.relativePath),
|
|
299
|
-
source: evidence.source,
|
|
300
|
-
action: dryRun ? "would-create" : "create",
|
|
301
|
-
});
|
|
302
|
-
assertPresetWriteScope(presetPackage, toPosix(evidence.relativePath));
|
|
303
|
-
if (dryRun) continue;
|
|
304
|
-
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
305
|
-
fs.writeFileSync(destinationPath, evidence.content);
|
|
306
|
-
}
|
|
307
|
-
for (const resource of presetContext.resourceFiles || []) {
|
|
308
|
-
const destinationPath = path.join(target.projectRoot, resource.relativePath);
|
|
309
|
-
changes.push({
|
|
310
|
-
destination: toPosix(resource.relativePath),
|
|
311
|
-
source: resource.source,
|
|
312
|
-
action: dryRun ? "would-create" : "create",
|
|
313
|
-
});
|
|
314
|
-
assertPresetWriteScope(presetPackage, toPosix(resource.relativePath));
|
|
315
|
-
if (dryRun) continue;
|
|
316
|
-
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
317
|
-
fs.writeFileSync(destinationPath, resource.content);
|
|
318
|
-
}
|
|
319
|
-
for (const [kind, rows] of Object.entries(presetContext.resourceIndexRows || {})) {
|
|
320
|
-
if (!rows.length) continue;
|
|
321
|
-
const destination = kind === "references" ? "references/INDEX.md" : "artifacts/INDEX.md";
|
|
322
|
-
const destinationPath = path.join(directory, destination);
|
|
323
|
-
const relativePath = toPosix(path.relative(target.projectRoot, destinationPath));
|
|
324
|
-
changes.push({
|
|
325
|
-
destination: relativePath,
|
|
326
|
-
source: `preset-${kind}-index`,
|
|
327
|
-
action: dryRun ? "would-update" : "update",
|
|
328
|
-
});
|
|
329
|
-
assertPresetWriteScope(presetPackage, relativePath);
|
|
330
|
-
if (dryRun) continue;
|
|
331
|
-
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
332
|
-
const existing = fs.existsSync(destinationPath) ? fs.readFileSync(destinationPath, "utf8") : "";
|
|
333
|
-
fs.writeFileSync(destinationPath, renderPresetResourceIndex(existing, kind, rows));
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
const task = {
|
|
337
|
-
id: taskIdForDirectory(target, directory),
|
|
338
|
-
shortId: normalizedTaskId,
|
|
339
|
-
title: taskTitle,
|
|
340
|
-
module: normalizedModuleKey || null,
|
|
341
|
-
path: `TARGET:${toPosix(path.relative(target.projectRoot, directory))}`,
|
|
342
|
-
locale: normalizedLocale,
|
|
343
|
-
budget: normalizedBudget,
|
|
344
|
-
kind: presetContext?.kind || "general",
|
|
345
|
-
preset: normalizedPreset,
|
|
346
|
-
presetVersion: presetContext?.presetVersion || "",
|
|
347
|
-
presetAudit: presetContext?.audit || null,
|
|
348
|
-
migrationTargetLevel: presetContext?.migrationTargetLevel || "",
|
|
349
|
-
migrationAchievedLevel: presetContext?.migrationAchievedLevel || "",
|
|
350
|
-
evidenceBundle: presetContext?.evidenceBundle || "",
|
|
351
|
-
longRunning,
|
|
352
|
-
};
|
|
353
|
-
const governance = syncTaskGovernance(target, task, { event: "new-task", state: "planned", message: "task registered by CLI", dryRun });
|
|
354
|
-
changes.push(...governance.changes);
|
|
355
|
-
const commandWriteScopes = governanceRelativePaths(changes);
|
|
356
|
-
if (presetContext) {
|
|
357
|
-
refreshPresetCommandAudit(target, presetContext, { commandWriteScopes, dryRun });
|
|
358
|
-
task.presetAudit = presetContext.audit;
|
|
359
|
-
}
|
|
360
|
-
const commit = commitGovernanceSync(governanceContext, commandWriteScopes, {
|
|
361
|
-
message: `chore(harness): register task ${task.id}`,
|
|
362
|
-
});
|
|
363
|
-
return {
|
|
364
|
-
dryRun,
|
|
365
|
-
task,
|
|
366
|
-
changes,
|
|
367
|
-
governance: { ...governance, commit },
|
|
368
|
-
};
|
|
369
|
-
} finally {
|
|
370
|
-
releaseGovernanceSync(governanceContext);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function refreshPresetCommandAudit(target, presetContext, { commandWriteScopes = [], dryRun = false } = {}) {
|
|
375
|
-
const scopes = [...new Set(commandWriteScopes.filter(Boolean))];
|
|
376
|
-
presetContext.audit = {
|
|
377
|
-
...presetContext.audit,
|
|
378
|
-
presetWriteScopes: presetContext.audit.writeScopes || [],
|
|
379
|
-
commandWriteScopes: scopes,
|
|
380
|
-
};
|
|
381
|
-
for (const evidence of presetContext.evidenceFiles || []) {
|
|
382
|
-
if (evidence.source !== "preset-audit") continue;
|
|
383
|
-
evidence.content = `${JSON.stringify(presetContext.audit, null, 2)}\n`;
|
|
384
|
-
if (dryRun) continue;
|
|
385
|
-
fs.writeFileSync(path.join(target.projectRoot, evidence.relativePath), evidence.content);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export function updateTaskLifecycle(targetInput, taskId, { event = "task-log", state = "", message = "", evidence = "" } = {}) {
|
|
390
|
-
const target = normalizeTarget(targetInput);
|
|
391
|
-
const taskDir = resolveTaskDirectory(target, taskId);
|
|
392
|
-
const progressPath = path.join(taskDir, "progress.md");
|
|
393
|
-
const registry = readCapabilityRegistry(target);
|
|
394
|
-
const normalizedState = state ? String(state).toLowerCase().replaceAll("-", "_") : "";
|
|
395
|
-
if (normalizedState && !allowedTaskStates.has(normalizedState)) throw new Error(`Invalid task state: ${state}`);
|
|
396
|
-
const currentTask = findTaskByDirectory(target, taskDir);
|
|
397
|
-
const canonicalTaskId = taskIdForDirectory(target, taskDir);
|
|
398
|
-
const budget = parseTaskBudget(readFileSafe(path.join(taskDir, "task_plan.md")));
|
|
399
|
-
validateLifecycleTransition({
|
|
400
|
-
event,
|
|
401
|
-
currentState: currentTask?.state || "unknown",
|
|
402
|
-
budget,
|
|
403
|
-
reviewContent: readFileSafe(path.join(taskDir, "review.md")),
|
|
404
|
-
reviewTaskKey: canonicalTaskId,
|
|
405
|
-
projectRoot: target.projectRoot,
|
|
406
|
-
taskDir,
|
|
407
|
-
});
|
|
408
|
-
if (event === "task-review") validateReviewEntryGate(taskDir, budget);
|
|
409
|
-
const governanceContext = beginGovernanceSync(target, { operation: `${event} ${canonicalTaskId}` });
|
|
410
|
-
try {
|
|
411
|
-
let content = readFileSafe(progressPath);
|
|
412
|
-
if (normalizedState) content = updateProgressState(content, normalizedState, registry.locale);
|
|
413
|
-
content = appendProgressLog(content, { event, message, evidence });
|
|
414
|
-
fs.writeFileSync(progressPath, content.endsWith("\n") ? content : `${content}\n`);
|
|
415
|
-
const allowedPaths = [toPosix(path.relative(target.projectRoot, progressPath))];
|
|
416
|
-
if (event === "task-review") {
|
|
417
|
-
const reviewPath = path.join(taskDir, "review.md");
|
|
418
|
-
const reviewContent = readFileSafe(reviewPath);
|
|
419
|
-
fs.writeFileSync(
|
|
420
|
-
reviewPath,
|
|
421
|
-
replaceAgentReviewSubmission(
|
|
422
|
-
reviewContent,
|
|
423
|
-
renderAgentReviewSubmission({
|
|
424
|
-
target,
|
|
425
|
-
taskDir,
|
|
426
|
-
canonicalTaskId,
|
|
427
|
-
message,
|
|
428
|
-
evidence,
|
|
429
|
-
}),
|
|
430
|
-
),
|
|
431
|
-
);
|
|
432
|
-
allowedPaths.push(toPosix(path.relative(target.projectRoot, reviewPath)));
|
|
433
|
-
}
|
|
434
|
-
const task =
|
|
435
|
-
findTaskByDirectory(target, taskDir) ||
|
|
436
|
-
{
|
|
437
|
-
id: canonicalTaskId,
|
|
438
|
-
shortId: path.basename(taskDir),
|
|
439
|
-
title: canonicalTaskId,
|
|
440
|
-
path: `TARGET:${toPosix(path.relative(target.projectRoot, taskDir))}`,
|
|
441
|
-
state: normalizedState || currentTask?.state || "unknown",
|
|
442
|
-
};
|
|
443
|
-
const governanceState = normalizedState || task.state || currentTask?.state || "planned";
|
|
444
|
-
const governance = syncTaskGovernance(target, task, { event, state: governanceState, message, dryRun: false });
|
|
445
|
-
const commit = commitGovernanceSync(governanceContext, [...allowedPaths, ...governanceRelativePaths(governance.changes)], {
|
|
446
|
-
message: `chore(harness): advance task ${canonicalTaskId} to ${governanceState}`,
|
|
447
|
-
});
|
|
448
|
-
return {
|
|
449
|
-
event,
|
|
450
|
-
task,
|
|
451
|
-
governance: { ...governance, commit },
|
|
452
|
-
};
|
|
453
|
-
} finally {
|
|
454
|
-
releaseGovernanceSync(governanceContext);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
export function confirmTaskReview(targetInput, taskId, { reviewer = "Human Reviewer", message = "", confirmText = "", evidence = "" } = {}) {
|
|
459
|
-
const target = normalizeTarget(targetInput);
|
|
460
|
-
const taskDir = resolveTaskDirectory(target, taskId);
|
|
461
|
-
return confirmTaskReviewWithContext({ target, taskDir, findTaskByDirectory }, { reviewer, message, confirmText, evidence });
|
|
462
|
-
}
|
|
463
|
-
function renderAgentReviewSubmission({ target, taskDir, canonicalTaskId, message, evidence }) {
|
|
464
|
-
const timestamp = nowTimestamp();
|
|
465
|
-
const submissionId = `ARS-${timestamp.replace(/[^0-9]/g, "").slice(0, 14)}`;
|
|
466
|
-
const materialsHash = hashTaskMaterials(taskDir);
|
|
467
|
-
const reviewContent = readFileSafe(path.join(taskDir, "review.md"));
|
|
468
|
-
const openFindings = collectReviewRisks(reviewContent).filter(isBlockingReviewRisk).length;
|
|
469
|
-
const evidenceSummary = evidence || message || "Agent submitted task for human review.";
|
|
470
|
-
return [
|
|
471
|
-
"## Agent Review Submission",
|
|
472
|
-
"",
|
|
473
|
-
"| Field | Value |",
|
|
474
|
-
"| --- | --- |",
|
|
475
|
-
`| Submission ID | ${submissionId} |`,
|
|
476
|
-
`| Submitted At | ${timestamp} |`,
|
|
477
|
-
"| Submitted By | agent |",
|
|
478
|
-
`| Task Key | ${canonicalTaskId} |`,
|
|
479
|
-
`| Materials Checklist Hash | ${materialsHash} |`,
|
|
480
|
-
`| Evidence Summary | ${markdownCell(evidenceSummary)} |`,
|
|
481
|
-
`| Open Findings Count | ${openFindings} |`,
|
|
482
|
-
`| Scanner Version | ${taskScannerVersion} |`,
|
|
483
|
-
`| Target | TARGET:${toPosix(path.relative(target.projectRoot, taskDir))} |`,
|
|
484
|
-
"",
|
|
485
|
-
].join("\n");
|
|
486
|
-
}
|
|
487
|
-
function replaceAgentReviewSubmission(content, block) {
|
|
488
|
-
const trimmed = String(content || "").trimEnd();
|
|
489
|
-
if (/^##\s*(?:Agent Review Submission|Agent 审查提交|Agent 提交审查)\s*$/im.test(trimmed)) {
|
|
490
|
-
return `${trimmed.replace(/^##\s*(?:Agent Review Submission|Agent 审查提交|Agent 提交审查)\s*$[\s\S]*?(?=^##\s+|(?![\s\S]))/im, `${block.trimEnd()}\n\n`)}\n`;
|
|
491
|
-
}
|
|
492
|
-
return `${trimmed}\n\n${block.trimEnd()}\n`;
|
|
493
|
-
}
|
|
494
|
-
function hashTaskMaterials(taskDir) {
|
|
495
|
-
const hash = crypto.createHash("sha256");
|
|
496
|
-
for (const fileName of ["brief.md", "task_plan.md", visualMapFile, lessonCandidatesFile, "progress.md", "review.md", "findings.md", longRunningTaskContractFile]) {
|
|
497
|
-
const filePath = path.join(taskDir, fileName);
|
|
498
|
-
if (!fs.existsSync(filePath)) continue;
|
|
499
|
-
hash.update(fileName);
|
|
500
|
-
hash.update("\0");
|
|
501
|
-
hash.update(readFileSafe(filePath));
|
|
502
|
-
hash.update("\0");
|
|
503
|
-
}
|
|
504
|
-
return hash.digest("hex").slice(0, 16);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
export function updateTaskPhase(targetInput, taskId, phaseId, { state = "", completion = "", evidenceStatus = "" } = {}) {
|
|
508
|
-
const target = normalizeTarget(targetInput);
|
|
509
|
-
const taskDir = resolveTaskDirectory(target, taskId);
|
|
510
|
-
const visualMapPath = path.join(taskDir, visualMapFile);
|
|
511
|
-
const legacyPath = path.join(taskDir, legacyVisualRoadmapFile);
|
|
512
|
-
if (!fs.existsSync(visualMapPath)) {
|
|
513
|
-
if (fs.existsSync(legacyPath)) throw new Error(`Task has legacy visual_roadmap.md only; rewrite it to visual_map.md before task-phase: ${taskId}`);
|
|
514
|
-
throw new Error(`Task visual map not found: ${taskId}`);
|
|
515
|
-
}
|
|
516
|
-
let content = readFileSafe(visualMapPath);
|
|
517
|
-
const normalizedState = state ? String(state).toLowerCase().replaceAll("-", "_") : "";
|
|
518
|
-
if (normalizedState && !allowedPhaseStates.has(normalizedState)) throw new Error(`Invalid phase state: ${state}`);
|
|
519
|
-
const normalizedEvidence = evidenceStatus ? String(evidenceStatus).toLowerCase() : "";
|
|
520
|
-
if (normalizedEvidence && !allowedEvidenceStatus.has(normalizedEvidence)) throw new Error(`Invalid evidence status: ${evidenceStatus}`);
|
|
521
|
-
const nextCompletion = completion === "" ? "" : Number.parseInt(String(completion), 10);
|
|
522
|
-
if (nextCompletion !== "" && (!Number.isInteger(nextCompletion) || nextCompletion < 0 || nextCompletion > 100)) {
|
|
523
|
-
throw new Error(`Invalid completion: ${completion}`);
|
|
524
|
-
}
|
|
525
|
-
const phaseUpdate = updateMarkdownTableRow(content, /^Phase ID$/i, (header, row) => {
|
|
526
|
-
const idIndex = getColumn(header, "Phase ID");
|
|
527
|
-
if ((row[idIndex] || "") !== phaseId) return null;
|
|
528
|
-
const next = [...row];
|
|
529
|
-
const stateIndex = getColumn(header, "State");
|
|
530
|
-
const completionIndex = getColumn(header, "Completion");
|
|
531
|
-
const evidenceIndex = getColumn(header, "Evidence Status");
|
|
532
|
-
if (normalizedState && stateIndex >= 0) next[stateIndex] = normalizedState;
|
|
533
|
-
if (nextCompletion !== "" && completionIndex >= 0) next[completionIndex] = String(nextCompletion);
|
|
534
|
-
if (normalizedEvidence && evidenceIndex >= 0) next[evidenceIndex] = normalizedEvidence;
|
|
535
|
-
return next;
|
|
536
|
-
});
|
|
537
|
-
if (!phaseUpdate.matched) throw new Error(`Phase not found: ${phaseId}`);
|
|
538
|
-
const governanceContext = beginGovernanceSync(target, { operation: `task-phase ${taskId} ${phaseId}` });
|
|
539
|
-
try {
|
|
540
|
-
content = phaseUpdate.content;
|
|
541
|
-
fs.writeFileSync(visualMapPath, content);
|
|
542
|
-
const commit = commitGovernanceSync(governanceContext, [toPosix(path.relative(target.projectRoot, visualMapPath))], {
|
|
543
|
-
message: `chore(harness): update task phase ${taskId} ${phaseId}`,
|
|
544
|
-
});
|
|
545
|
-
return { event: "task-phase", task: findTaskByDirectory(target, taskDir), phaseId, governance: { commit } };
|
|
546
|
-
} finally {
|
|
547
|
-
releaseGovernanceSync(governanceContext);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
export function updateModuleStep(targetInput, moduleKey, stepId, { state = "" } = {}) {
|
|
552
|
-
const target = normalizeTarget(targetInput);
|
|
553
|
-
const normalizedModuleKey = normalizeTaskId(moduleKey);
|
|
554
|
-
const normalizedState = String(state || "done").toLowerCase().replaceAll("_", "-");
|
|
555
|
-
if (!["planned", "in-progress", "done", "blocked", "superseded"].includes(normalizedState)) throw new Error(`Invalid module step state: ${state}`);
|
|
556
|
-
const modulePlanPath = path.join(target.docsRoot, "09-PLANNING/MODULES", normalizedModuleKey, "module_plan.md");
|
|
557
|
-
if (!fs.existsSync(modulePlanPath)) throw new Error(`Module plan not found: ${normalizedModuleKey}`);
|
|
558
|
-
let content = readFileSafe(modulePlanPath);
|
|
559
|
-
const stepUpdate = updateMarkdownTableRow(content, /^(Step ID|步骤 ID)$/i, (header, row) => {
|
|
560
|
-
const idIndex = firstColumn(header, ["Step ID", "步骤 ID"]);
|
|
561
|
-
if ((row[idIndex] || "") !== stepId) return null;
|
|
562
|
-
const next = [...row];
|
|
563
|
-
const statusIndex = firstColumn(header, ["Status", "状态"]);
|
|
564
|
-
if (statusIndex >= 0) next[statusIndex] = normalizedState;
|
|
565
|
-
return next;
|
|
566
|
-
});
|
|
567
|
-
if (!stepUpdate.matched) throw new Error(`Module step not found: ${stepId}`);
|
|
568
|
-
const governanceContext = beginGovernanceSync(target, { operation: `module-step ${normalizedModuleKey} ${stepId}` });
|
|
569
|
-
try {
|
|
570
|
-
content = stepUpdate.content;
|
|
571
|
-
fs.writeFileSync(modulePlanPath, content);
|
|
572
|
-
|
|
573
|
-
const registryPath = path.join(target.docsRoot, "09-PLANNING/Module-Registry.md");
|
|
574
|
-
if (fs.existsSync(registryPath)) {
|
|
575
|
-
let registry = readFileSafe(registryPath);
|
|
576
|
-
const registryUpdate = updateMarkdownTableRow(registry, /^(ID|模块 Key)$/i, (header, row) => {
|
|
577
|
-
const moduleIndex = firstColumn(header, ["Module", "模块", "模块 Key"]);
|
|
578
|
-
const taskPlanIndex = getColumn(header, "Task Plan");
|
|
579
|
-
const matchesModule = normalizeTaskId(row[moduleIndex] || "") === normalizedModuleKey;
|
|
580
|
-
const matchesPlan = taskPlanIndex >= 0 && String(row[taskPlanIndex] || "").includes(`/MODULES/${normalizedModuleKey}/`);
|
|
581
|
-
if (!matchesModule && !matchesPlan) return null;
|
|
582
|
-
const next = [...row];
|
|
583
|
-
const statusIndex = firstColumn(header, ["Status", "状态"]);
|
|
584
|
-
const updatedIndex = firstColumn(header, ["Updated", "更新时间"]);
|
|
585
|
-
const currentStepIndex = firstColumn(header, ["Current Step", "当前步骤"]);
|
|
586
|
-
const chineseRegistry = header.some((cell) => /模块 Key|模块名称|状态|更新时间/.test(cell));
|
|
587
|
-
if (statusIndex >= 0) {
|
|
588
|
-
next[statusIndex] = normalizedState === "done"
|
|
589
|
-
? chineseRegistry ? "completed" : "merged"
|
|
590
|
-
: normalizedState === "in-progress" ? chineseRegistry ? "in-progress" : "active" : normalizedState;
|
|
591
|
-
}
|
|
592
|
-
if (currentStepIndex >= 0) next[currentStepIndex] = stepId;
|
|
593
|
-
if (updatedIndex >= 0) next[updatedIndex] = todayDate();
|
|
594
|
-
return next;
|
|
595
|
-
});
|
|
596
|
-
registry = registryUpdate.content;
|
|
597
|
-
fs.writeFileSync(registryPath, registry);
|
|
598
|
-
}
|
|
599
|
-
const governance = syncModuleStepGovernance(target, { moduleKey: normalizedModuleKey, stepId, state: normalizedState });
|
|
600
|
-
const commit = commitGovernanceSync(
|
|
601
|
-
governanceContext,
|
|
602
|
-
[
|
|
603
|
-
toPosix(path.relative(target.projectRoot, modulePlanPath)),
|
|
604
|
-
toPosix(path.relative(target.projectRoot, registryPath)),
|
|
605
|
-
...governanceRelativePaths(governance.changes),
|
|
606
|
-
],
|
|
607
|
-
{ message: `chore(harness): update module ${normalizedModuleKey} step ${stepId}` },
|
|
608
|
-
);
|
|
609
|
-
return { event: "module-step", moduleKey: normalizedModuleKey, stepId, state: normalizedState, governance: { ...governance, commit } };
|
|
610
|
-
} finally {
|
|
611
|
-
releaseGovernanceSync(governanceContext);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
export function listLifecycleTasks(targetInput, { state = "", moduleKey = "", queue = "", preset = "", review = "", lesson = "", search = "", missingMaterials = false } = {}) {
|
|
616
|
-
const target = normalizeTarget(targetInput);
|
|
617
|
-
let tasks = collectTasks(target);
|
|
618
|
-
if (state) tasks = tasks.filter((task) => task.state === String(state).toLowerCase().replaceAll("-", "_"));
|
|
619
|
-
if (moduleKey) tasks = tasks.filter((task) => task.module === normalizeTaskId(moduleKey));
|
|
620
|
-
if (queue) {
|
|
621
|
-
const normalizedQueue = queryToken(queue);
|
|
622
|
-
tasks = tasks.filter((task) => (task.taskQueues || []).map(queryToken).includes(normalizedQueue));
|
|
623
|
-
}
|
|
624
|
-
if (preset) tasks = tasks.filter((task) => queryToken(task.taskPreset || "none") === queryToken(preset));
|
|
625
|
-
if (review) tasks = tasks.filter((task) => queryToken(task.reviewStatus || "") === queryToken(review));
|
|
626
|
-
if (lesson) {
|
|
627
|
-
const needle = queryToken(lesson);
|
|
628
|
-
tasks = tasks.filter((task) => [task.lessonCandidateStatus, task.lessonCandidateReviewDecision, task.lessonCandidatePromotionState].some((value) => queryToken(value) === needle));
|
|
629
|
-
}
|
|
630
|
-
if (missingMaterials) tasks = tasks.filter((task) => !task.materialsReady);
|
|
631
|
-
if (search) {
|
|
632
|
-
const needle = String(search).toLowerCase();
|
|
633
|
-
tasks = tasks.filter((task) => [
|
|
634
|
-
task.id,
|
|
635
|
-
task.taskKey,
|
|
636
|
-
task.shortId,
|
|
637
|
-
task.title,
|
|
638
|
-
task.currentPath,
|
|
639
|
-
task.taskPlanPath,
|
|
640
|
-
task.module,
|
|
641
|
-
task.inferredModule,
|
|
642
|
-
].some((value) => String(value || "").toLowerCase().includes(needle)));
|
|
643
|
-
}
|
|
644
|
-
return { tasks };
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
function queryToken(value) {
|
|
648
|
-
return String(value || "").trim().toLowerCase().replaceAll("_", "-");
|
|
649
|
-
}
|