coding-agent-harness 1.0.1 → 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.
- package/CHANGELOG.md +44 -0
- package/CONTRIBUTING.md +98 -0
- package/README.en-US.md +14 -0
- package/README.md +230 -80
- package/README.zh-CN.md +290 -0
- package/SKILL.md +132 -198
- package/docs-release/README.md +80 -9
- package/docs-release/architecture/overview.md +298 -28
- package/docs-release/architecture/overview.zh-CN.md +292 -0
- package/docs-release/assets/dashboard-overview.png +0 -0
- package/docs-release/assets/harness-architecture.svg +163 -0
- package/docs-release/assets/harness-workflow.svg +64 -0
- package/docs-release/guides/agent-installation.en-US.md +237 -0
- package/docs-release/guides/agent-installation.md +149 -27
- 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 +113 -0
- package/docs-release/guides/document-audience-and-surfaces.md +113 -0
- package/docs-release/guides/full-legacy-migration-subagent-strategy.md +334 -0
- package/docs-release/guides/full-legacy-migration-subagent-strategy.zh-CN.md +334 -0
- package/docs-release/guides/legacy-migration-agent-prompt.md +373 -0
- package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +350 -0
- package/docs-release/guides/migration-playbook.en-US.md +324 -0
- package/docs-release/guides/migration-playbook.md +328 -0
- package/docs-release/guides/parent-control-repository-pattern.en-US.md +254 -0
- package/docs-release/guides/parent-control-repository-pattern.md +254 -0
- package/docs-release/guides/preset-development.md +214 -0
- package/docs-release/guides/repository-operating-models.en-US.md +197 -0
- package/docs-release/guides/repository-operating-models.md +197 -0
- package/docs-release/guides/task-state-machine.en-US.md +207 -0
- package/docs-release/guides/task-state-machine.md +214 -0
- package/docs-release/intl/README.md +15 -0
- package/docs-release/intl/de-DE.md +18 -0
- package/docs-release/intl/en-US.md +18 -0
- package/docs-release/intl/es-ES.md +18 -0
- package/docs-release/intl/fr-FR.md +18 -0
- package/docs-release/intl/ja-JP.md +18 -0
- package/docs-release/intl/ko-KR.md +18 -0
- package/docs-release/intl/zh-CN.md +18 -0
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/brief.md +13 -0
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/findings.md +7 -0
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/lesson_candidates.md +24 -0
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +1 -1
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/task_plan.md +4 -2
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/{visual_roadmap.md → visual_map.md} +9 -1
- package/package.json +10 -3
- 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 +5 -5
- package/references/delivery-operating-model-standard.md +3 -3
- package/references/docs-directory-standard.md +53 -10
- package/references/external-source-intake-standard.md +75 -0
- package/references/harness-ledger.md +53 -94
- package/references/legacy-12-phase-bootstrap.md +41 -0
- package/references/lessons-governance.md +100 -88
- package/references/module-parallel-standard.md +14 -14
- package/references/planning-loop.md +51 -7
- package/references/project-onboarding-audit.md +10 -0
- package/references/pull-request-standard.md +118 -0
- package/references/repo-governance-standard.md +12 -1
- 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/testing-standard.md +50 -0
- package/references/walkthrough-closeout.md +10 -9
- package/scripts/check-harness.mjs +111 -331
- package/scripts/commands/dashboard-command.mjs +67 -0
- package/scripts/commands/migration-command.mjs +96 -0
- package/scripts/commands/preset-command.mjs +73 -0
- package/scripts/commands/task-command.mjs +327 -0
- package/scripts/harness.mjs +106 -20
- package/scripts/lib/capability-registry.mjs +591 -0
- package/scripts/lib/check-module-parallel.mjs +237 -0
- package/scripts/lib/check-profiles.mjs +418 -0
- package/scripts/lib/check-task-contracts.mjs +47 -0
- package/scripts/lib/core-shared.mjs +196 -0
- package/scripts/lib/dashboard-data.mjs +412 -0
- package/scripts/lib/dashboard-workbench.mjs +257 -0
- package/scripts/lib/dashboard-writer.mjs +107 -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 +514 -0
- package/scripts/lib/governance-table-boundary.mjs +175 -0
- package/scripts/lib/harness-core.mjs +15 -1318
- package/scripts/lib/lesson-maintenance.mjs +152 -0
- package/scripts/lib/markdown-utils.mjs +158 -0
- package/scripts/lib/migration-planner.mjs +478 -0
- package/scripts/lib/migration-support.mjs +312 -0
- package/scripts/lib/preset-audit-contracts.mjs +37 -0
- package/scripts/lib/preset-engine.mjs +497 -0
- package/scripts/lib/preset-registry.mjs +627 -0
- package/scripts/lib/preset-resource-contracts.mjs +83 -0
- package/scripts/lib/review-confirm-git-gate.mjs +248 -0
- package/scripts/lib/status-dashboard-renderer.mjs +102 -0
- package/scripts/lib/subagent-authorization-audit.mjs +196 -0
- package/scripts/lib/task-completion-consistency.mjs +16 -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/review-confirm.mjs +101 -0
- package/scripts/lib/task-lifecycle/review-gates.mjs +70 -0
- package/scripts/lib/task-lifecycle/text-utils.mjs +24 -0
- package/scripts/lib/task-lifecycle.mjs +649 -0
- package/scripts/lib/task-review-model.mjs +469 -0
- package/scripts/lib/task-scanner.mjs +576 -0
- 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 +32 -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/{templates/planning/visual_roadmap.md → skills/preset-creator/references/complex-task-skeleton/visual_map.md} +24 -2
- package/skills/preset-creator/references/preset-package-skeleton.md +296 -0
- package/templates/AGENTS.md.template +51 -36
- package/templates/architecture/Architecture-SSoT.md +21 -0
- package/templates/architecture/README.md +49 -0
- package/templates/architecture/critical-flows.md +22 -0
- package/templates/architecture/local-repo-context.md +20 -0
- package/templates/architecture/service-catalog.md +17 -0
- package/templates/architecture/services/service-template.md +31 -0
- package/templates/architecture/system-map.md +22 -0
- package/templates/dashboard/assets/app-src/00-state.js +42 -0
- package/templates/dashboard/assets/app-src/10-router.js +77 -0
- package/templates/dashboard/assets/app-src/20-overview.js +241 -0
- package/templates/dashboard/assets/app-src/30-tasks.js +409 -0
- package/templates/dashboard/assets/app-src/35-task-detail.js +246 -0
- package/templates/dashboard/assets/app-src/40-modules.js +58 -0
- package/templates/dashboard/assets/app-src/45-review.js +347 -0
- package/templates/dashboard/assets/app-src/50-migration.js +183 -0
- package/templates/dashboard/assets/app-src/60-shared.js +61 -0
- package/templates/dashboard/assets/app-src/90-bindings.js +524 -0
- package/templates/dashboard/assets/app.css +3107 -300
- package/templates/dashboard/assets/app.css.manifest.json +9 -0
- package/templates/dashboard/assets/app.js +2068 -306
- package/templates/dashboard/assets/app.manifest.json +12 -0
- package/templates/dashboard/assets/css-src/00-foundation.css +342 -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 +427 -0
- package/templates/dashboard/assets/css-src/50-responsive-overrides.css +551 -0
- package/templates/dashboard/assets/i18n.js +531 -44
- package/templates/dashboard/assets/mermaid-renderer.js +58 -8
- package/templates/development/README.md +52 -0
- package/templates/development/codebase-map.md +11 -0
- package/templates/development/cross-repo-debugging.md +18 -0
- package/templates/development/external-context/service-template.md +33 -0
- package/templates/development/external-source-packs/README.md +24 -0
- package/templates/development/external-source-packs/digest-template.md +28 -0
- package/templates/development/local-setup.md +16 -0
- package/templates/development/stubs-and-mocks.md +11 -0
- package/templates/integrations/README.md +40 -0
- package/templates/integrations/api-contract.md +42 -0
- package/templates/integrations/event-contract.md +46 -0
- package/templates/integrations/third-party/vendor-template.md +42 -0
- package/templates/integrations/webhook-contract.md +41 -0
- 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/brief.md +32 -0
- package/templates/planning/execution_strategy.md +31 -0
- package/templates/planning/lesson_candidates.md +70 -0
- package/templates/planning/long-running-task-contract.md +7 -0
- package/templates/planning/module_brief.md +25 -0
- package/templates/planning/module_session_prompt.md +6 -0
- package/templates/planning/optional/artifacts/INDEX.md +3 -3
- package/templates/planning/optional/references/INDEX.md +3 -3
- package/templates/planning/review.md +59 -0
- package/templates/planning/task_plan.md +40 -15
- package/templates/planning/visual_map.md +50 -0
- package/templates/reference/docs-library-standard.md +31 -0
- package/templates/reference/execution-workflow-standard.md +5 -2
- package/templates/reference/external-source-intake-standard.md +82 -0
- package/templates/reference/harness-ledger-standard.md +1 -0
- package/templates/reference/pull-request-standard.md +80 -0
- package/templates/reference/repo-governance-standard.md +8 -5
- package/templates/reference/review-routing-standard.md +6 -0
- package/templates/reference/walkthrough-standard.md +3 -1
- package/templates/verifier/verifier-output.md +1 -1
- package/templates/walkthrough/walkthrough-template.md +2 -2
- package/templates-zh-CN/AGENTS.md.template +73 -70
- package/templates-zh-CN/architecture/Architecture-SSoT.md +21 -0
- package/templates-zh-CN/architecture/README.md +51 -0
- package/templates-zh-CN/architecture/critical-flows.md +24 -0
- package/templates-zh-CN/architecture/local-repo-context.md +20 -0
- package/templates-zh-CN/architecture/service-catalog.md +17 -0
- package/templates-zh-CN/architecture/services/service-template.md +31 -0
- package/templates-zh-CN/architecture/system-map.md +22 -0
- package/templates-zh-CN/development/README.md +54 -0
- package/templates-zh-CN/development/codebase-map.md +11 -0
- package/templates-zh-CN/development/cross-repo-debugging.md +18 -0
- package/templates-zh-CN/development/external-context/service-template.md +33 -0
- package/templates-zh-CN/development/external-source-packs/README.md +24 -0
- package/templates-zh-CN/development/external-source-packs/digest-template.md +28 -0
- package/templates-zh-CN/development/local-setup.md +16 -0
- package/templates-zh-CN/development/stubs-and-mocks.md +11 -0
- package/templates-zh-CN/integrations/README.md +42 -0
- package/templates-zh-CN/integrations/api-contract.md +42 -0
- package/templates-zh-CN/integrations/event-contract.md +46 -0
- package/templates-zh-CN/integrations/third-party/vendor-template.md +42 -0
- package/templates-zh-CN/integrations/webhook-contract.md +41 -0
- package/templates-zh-CN/ledger/Harness-Ledger.md +17 -40
- package/templates-zh-CN/planning/brief.md +32 -0
- package/templates-zh-CN/planning/execution_strategy.md +30 -0
- package/templates-zh-CN/planning/lesson_candidates.md +70 -0
- package/templates-zh-CN/planning/long-running-task-contract.md +1 -1
- package/templates-zh-CN/planning/module_brief.md +25 -0
- package/templates-zh-CN/planning/module_plan.md +2 -2
- package/templates-zh-CN/planning/module_session_prompt.md +4 -3
- package/templates-zh-CN/planning/review.md +59 -1
- package/templates-zh-CN/planning/task_plan.md +37 -11
- package/templates-zh-CN/planning/{visual_roadmap.md → visual_map.md} +21 -2
- package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
- package/templates-zh-CN/reference/docs-library-standard.md +36 -1
- package/templates-zh-CN/reference/execution-workflow-standard.md +10 -2
- package/templates-zh-CN/reference/external-source-intake-standard.md +82 -0
- package/templates-zh-CN/reference/harness-ledger-standard.md +7 -4
- package/templates-zh-CN/reference/pull-request-standard.md +106 -0
- package/templates-zh-CN/reference/repo-governance-standard.md +4 -1
- package/templates-zh-CN/reference/review-routing-standard.md +8 -1
- package/templates-zh-CN/reference/walkthrough-standard.md +6 -5
- package/templates-zh-CN/walkthrough/Closeout-SSoT.md +2 -2
- package/templates-zh-CN/walkthrough/walkthrough-template.md +2 -2
- package/scripts/smoke-dashboard.mjs +0 -70
- package/scripts/test-harness.mjs +0 -483
- package/templates/ssot/Feature-SSoT.md +0 -43
- package/templates/ssot/Lessons-SSoT.md +0 -44
- package/templates-zh-CN/dashboard/assets/app.css +0 -399
- package/templates-zh-CN/dashboard/assets/app.js +0 -435
- package/templates-zh-CN/dashboard/assets/i18n.js +0 -47
- package/templates-zh-CN/dashboard/assets/markdown-reader.js +0 -116
- package/templates-zh-CN/dashboard/assets/mermaid-renderer.js +0 -59
- package/templates-zh-CN/dashboard/index.html +0 -18
- package/templates-zh-CN/ssot/Feature-SSoT.md +0 -49
- package/templates-zh-CN/ssot/Lessons-SSoT.md +0 -49
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
lessonCandidatesFile,
|
|
5
|
+
nowTimestamp,
|
|
6
|
+
readFileSafe,
|
|
7
|
+
} from "../core-shared.mjs";
|
|
8
|
+
import {
|
|
9
|
+
collectReviewRisks,
|
|
10
|
+
isBlockingReviewRisk,
|
|
11
|
+
isLessonCandidateDecisionComplete,
|
|
12
|
+
parseLessonCandidateStatus,
|
|
13
|
+
parseTaskBudget,
|
|
14
|
+
taskIdForDirectory,
|
|
15
|
+
} from "../task-scanner.mjs";
|
|
16
|
+
import { commitReviewConfirmationGate, prepareReviewConfirmGitGate } from "../review-confirm-git-gate.mjs";
|
|
17
|
+
import { validateHumanReviewConfirmation } from "./review-gates.mjs";
|
|
18
|
+
import { appendProgressLog, markdownCell } from "./text-utils.mjs";
|
|
19
|
+
|
|
20
|
+
export function confirmTaskReview({ target, taskDir, findTaskByDirectory }, { reviewer = "Human Reviewer", message = "", confirmText = "", evidence = "" } = {}) {
|
|
21
|
+
assertTaskDirectoryInsidePlanning(target, taskDir);
|
|
22
|
+
const canonicalTaskId = taskIdForDirectory(target, taskDir);
|
|
23
|
+
const shortId = path.basename(taskDir);
|
|
24
|
+
if (confirmText && ![shortId, canonicalTaskId].includes(confirmText)) {
|
|
25
|
+
throw new Error(`Review confirmation text must match task id: ${shortId}`);
|
|
26
|
+
}
|
|
27
|
+
if (!confirmText) throw new Error(`Missing review confirmation text: ${shortId}`);
|
|
28
|
+
|
|
29
|
+
const reviewPath = path.join(taskDir, "review.md");
|
|
30
|
+
const progressPath = path.join(taskDir, "progress.md");
|
|
31
|
+
const reviewContent = readFileSafe(reviewPath);
|
|
32
|
+
const budget = parseTaskBudget(readFileSafe(path.join(taskDir, "task_plan.md")));
|
|
33
|
+
const candidateStatus = parseLessonCandidateStatus(readFileSafe(path.join(taskDir, lessonCandidatesFile)));
|
|
34
|
+
const blockingRisks = collectReviewRisks(reviewContent).filter(isBlockingReviewRisk);
|
|
35
|
+
if (blockingRisks.length > 0) {
|
|
36
|
+
const ids = blockingRisks.map((risk) => risk.id || risk.severity).join(", ");
|
|
37
|
+
throw new Error(`Open blocking review findings must be closed before confirmation: ${ids}`);
|
|
38
|
+
}
|
|
39
|
+
validateHumanReviewConfirmation({
|
|
40
|
+
task: findTaskByDirectory(target, taskDir),
|
|
41
|
+
budget,
|
|
42
|
+
});
|
|
43
|
+
if (budget !== "simple" && !isLessonCandidateDecisionComplete(candidateStatus)) {
|
|
44
|
+
throw new Error(`Human review confirmation requires lesson candidate decision complete; current status is ${candidateStatus.status}.`);
|
|
45
|
+
}
|
|
46
|
+
const gitGate = prepareReviewConfirmGitGate(target.projectRoot, [reviewPath, progressPath]);
|
|
47
|
+
|
|
48
|
+
const timestamp = nowTimestamp();
|
|
49
|
+
const confirmationId = `HRC-${timestamp.replace(/[^0-9]/g, "").slice(0, 14)}`;
|
|
50
|
+
const safeReviewer = markdownCell(reviewer || "Human Reviewer");
|
|
51
|
+
const safeReviewerEmail = markdownCell(process.env.GIT_AUTHOR_EMAIL || process.env.GIT_COMMITTER_EMAIL || "reviewer@example.invalid");
|
|
52
|
+
const safeMessage = markdownCell(message || "Human review confirmed");
|
|
53
|
+
const safeEvidence = markdownCell(evidence || `TARGET:docs/09-PLANNING/${canonicalTaskId}/review.md`);
|
|
54
|
+
const renderConfirmationBlock = ({ commitSha = "pending", auditStatus = "commit-pending" } = {}) =>
|
|
55
|
+
`## Human Review Confirmation\n\n| Field | Value |\n| --- | --- |\n| Confirmation ID | ${confirmationId} |\n| Confirmed At | ${timestamp} |\n| Reviewer | ${safeReviewer} |\n| Reviewer Email | ${safeReviewerEmail} |\n| Task Key | ${canonicalTaskId} |\n| Confirm Text | ${markdownCell(confirmText)} |\n| Evidence Checked | ${safeEvidence} |\n| Commit SHA | ${markdownCell(commitSha)} |\n| Audit Status | ${markdownCell(auditStatus)} |\n| Message | ${safeMessage} |\n`;
|
|
56
|
+
const confirmationBlock = renderConfirmationBlock();
|
|
57
|
+
const nextReview = replaceReviewConfirmation(reviewContent, confirmationBlock);
|
|
58
|
+
fs.writeFileSync(reviewPath, nextReview.endsWith("\n") ? nextReview : `${nextReview}\n`);
|
|
59
|
+
let progressContent = readFileSafe(progressPath);
|
|
60
|
+
progressContent = appendProgressLog(progressContent, {
|
|
61
|
+
event: "review-confirm",
|
|
62
|
+
message: message || `Human review confirmed by ${reviewer}`,
|
|
63
|
+
evidence: evidence || `TARGET:docs/09-PLANNING/${canonicalTaskId}/review.md`,
|
|
64
|
+
actor: reviewer || "Human Reviewer",
|
|
65
|
+
});
|
|
66
|
+
fs.writeFileSync(progressPath, progressContent.endsWith("\n") ? progressContent : `${progressContent}\n`);
|
|
67
|
+
const audit = commitReviewConfirmationGate(gitGate, {
|
|
68
|
+
taskId: canonicalTaskId,
|
|
69
|
+
reviewPath,
|
|
70
|
+
message: message || `Human review confirmed by ${reviewer}`,
|
|
71
|
+
writeFinalAudit(commitSha) {
|
|
72
|
+
const currentReview = readFileSafe(reviewPath);
|
|
73
|
+
const finalReview = replaceReviewConfirmation(currentReview, renderConfirmationBlock({ commitSha, auditStatus: "committed" }));
|
|
74
|
+
fs.writeFileSync(reviewPath, finalReview.endsWith("\n") ? finalReview : `${finalReview}\n`);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
event: "review-confirm",
|
|
79
|
+
task: findTaskByDirectory(target, taskDir) || { id: canonicalTaskId, reviewStatus: "confirmed" },
|
|
80
|
+
audit,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function assertTaskDirectoryInsidePlanning(target, taskDir) {
|
|
85
|
+
const realTaskDir = fs.realpathSync(taskDir);
|
|
86
|
+
const allowedRoots = [
|
|
87
|
+
path.join(target.docsRoot, "09-PLANNING/TASKS"),
|
|
88
|
+
path.join(target.docsRoot, "09-PLANNING/MODULES"),
|
|
89
|
+
].filter(fs.existsSync).map((root) => fs.realpathSync(root));
|
|
90
|
+
if (!allowedRoots.some((root) => realTaskDir === root || realTaskDir.startsWith(`${root}${path.sep}`))) {
|
|
91
|
+
throw new Error(`Task directory outside planning root: ${taskIdForDirectory(target, taskDir)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function replaceReviewConfirmation(content, block) {
|
|
96
|
+
const trimmed = String(content || "").trimEnd();
|
|
97
|
+
if (/^##\s*(?:Human Review Confirmation|人工审查确认)\s*$/im.test(trimmed)) {
|
|
98
|
+
return trimmed.replace(/^##\s*(?:Human Review Confirmation|人工审查确认)\s*$[\s\S]*?(?=^##\s+|(?![\s\S]))/im, `${block.trimEnd()}\n\n`);
|
|
99
|
+
}
|
|
100
|
+
return `${trimmed}\n\n${block.trimEnd()}\n`;
|
|
101
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
lessonCandidatesFile,
|
|
5
|
+
} from "../core-shared.mjs";
|
|
6
|
+
import {
|
|
7
|
+
collectReviewRisks,
|
|
8
|
+
isBlockingReviewRisk,
|
|
9
|
+
parsePhases,
|
|
10
|
+
parseReviewConfirmation,
|
|
11
|
+
readVisualMapContractFile,
|
|
12
|
+
} from "../task-scanner.mjs";
|
|
13
|
+
|
|
14
|
+
export function validateLifecycleTransition({ event, currentState, budget, reviewContent = "", reviewTaskKey = "", projectRoot = "", taskDir = "" }) {
|
|
15
|
+
if (event === "task-review" && currentState !== "in_progress") {
|
|
16
|
+
throw new Error(`task-review requires current state in_progress; current state is ${currentState || "unknown"}`);
|
|
17
|
+
}
|
|
18
|
+
if (event === "task-complete" && budget !== "simple" && currentState !== "review") {
|
|
19
|
+
throw new Error(`task-complete for ${budget} tasks requires current state review. Run task-review first.`);
|
|
20
|
+
}
|
|
21
|
+
if (event === "task-complete" && budget !== "simple") {
|
|
22
|
+
const blockingRisks = collectReviewRisks(reviewContent).filter(isBlockingReviewRisk);
|
|
23
|
+
if (blockingRisks.length > 0) {
|
|
24
|
+
const ids = blockingRisks.map((risk) => risk.id || risk.severity).join(", ");
|
|
25
|
+
throw new Error(`Open blocking review findings must be closed before task-complete: ${ids}`);
|
|
26
|
+
}
|
|
27
|
+
if (!parseReviewConfirmation(reviewContent, { taskKey: reviewTaskKey, projectRoot, taskDir })?.confirmed) {
|
|
28
|
+
throw new Error("Human review must be confirmed before task-complete. Run review-confirm first.");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function validateReviewEntryGate(taskDir, budget) {
|
|
34
|
+
if (budget === "simple") return;
|
|
35
|
+
const candidatePath = path.join(taskDir, lessonCandidatesFile);
|
|
36
|
+
if (!fs.existsSync(candidatePath)) {
|
|
37
|
+
throw new Error(`task-review requires ${lessonCandidatesFile} before entering human review.`);
|
|
38
|
+
}
|
|
39
|
+
const phases = parsePhases(readVisualMapContractFile(taskDir).content);
|
|
40
|
+
const actionablePhases = phases.filter((phase) => phase.state !== "skipped");
|
|
41
|
+
const hasRecordedPhaseProgress = actionablePhases.some(
|
|
42
|
+
(phase) =>
|
|
43
|
+
phase.completion > 0 ||
|
|
44
|
+
["in_progress", "review", "blocked", "done"].includes(phase.state) ||
|
|
45
|
+
["partial", "present", "waived"].includes(phase.evidenceStatus),
|
|
46
|
+
);
|
|
47
|
+
if (actionablePhases.length > 0 && !hasRecordedPhaseProgress) {
|
|
48
|
+
throw new Error("task-review requires at least one Visual Map phase progress update. Run task-phase before entering human review.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function validateHumanReviewConfirmation({ task, budget }) {
|
|
53
|
+
if (budget === "simple") return;
|
|
54
|
+
if (!task?.walkthroughPath) {
|
|
55
|
+
throw new Error("Human review confirmation requires a walkthrough linked from Closeout SSoT before review-confirm.");
|
|
56
|
+
}
|
|
57
|
+
const queueState = task?.reviewQueueState || "not-in-queue";
|
|
58
|
+
if (queueState !== "ready-to-confirm") {
|
|
59
|
+
const state = task?.state || "unknown";
|
|
60
|
+
throw new Error(`Human review confirmation requires canonical ready-to-confirm review queue; current state is ${state}, review queue is ${queueState}.`);
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(task?.taskQueues) || !task.taskQueues.includes("review")) {
|
|
63
|
+
const queues = Array.isArray(task?.taskQueues) ? task.taskQueues.join(", ") : "none";
|
|
64
|
+
throw new Error(`Human review confirmation requires the task to be in the Review queue; current queues: ${queues || "none"}.`);
|
|
65
|
+
}
|
|
66
|
+
if (!task?.lessonCandidateDecisionComplete) {
|
|
67
|
+
const status = task?.lessonCandidateStatus || "missing";
|
|
68
|
+
throw new Error(`Human review confirmation requires lesson candidate decision complete; current status is ${status}.`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { nowTimestamp } from "../core-shared.mjs";
|
|
2
|
+
|
|
3
|
+
export function appendProgressLog(content, { event, message, evidence, actor = "coordinator" }) {
|
|
4
|
+
const timestamp = nowTimestamp();
|
|
5
|
+
const safeMessage = String(message || event).replace(/\r?\n/g, " ").trim();
|
|
6
|
+
const safeEvidence = String(evidence || "n/a").replace(/\r?\n/g, " ").trim();
|
|
7
|
+
if (/^##\s*Log\s*$/im.test(content)) {
|
|
8
|
+
return content.replace(
|
|
9
|
+
/(^##\s*Log\s*$[\s\S]*?\| --- \| --- \| --- \| --- \| --- \|\n)/im,
|
|
10
|
+
`$1| ${timestamp} | ${actor} | ${event}: ${safeMessage} | ${safeEvidence} | ${event === "task-complete" ? "done" : "continue"} |\n`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
if (/^##\s*进度记录\s*$/im.test(content)) {
|
|
14
|
+
return `${content.trimEnd()}\n\n### [${timestamp}] - ${event}\n\n- 做了什么:${safeMessage}\n- 验证结果:已记录\n- 下一步:${event === "task-complete" ? "完成" : "继续执行"}\n- 证据:${safeEvidence}\n`;
|
|
15
|
+
}
|
|
16
|
+
return `${content.trimEnd()}\n\n## Log\n\n| Time | Actor | Action | Evidence | Next |\n| --- | --- | --- | --- | --- |\n| ${timestamp} | ${actor} | ${event}: ${safeMessage} | ${safeEvidence} | ${event === "task-complete" ? "done" : "continue"} |\n`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function markdownCell(value) {
|
|
20
|
+
return String(value || "")
|
|
21
|
+
.replace(/\r?\n/g, " ")
|
|
22
|
+
.replaceAll("|", "\\|")
|
|
23
|
+
.trim();
|
|
24
|
+
}
|