coding-agent-harness 1.0.7 → 1.1.0
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 +33 -0
- package/CONTRIBUTING.md +9 -5
- package/README.md +12 -2
- package/README.zh-CN.md +10 -2
- package/SKILL.md +14 -3
- package/dist/build-dist.mjs +32 -6
- package/dist/check-dist-observation.mjs +73 -28
- package/dist/check-harness.mjs +0 -1
- package/dist/check-import-graph.mjs +44 -27
- package/dist/check-lite-forbidden-surfaces.mjs +121 -0
- package/dist/check-no-ts-nocheck.mjs +88 -0
- package/dist/check-runtime-emit.mjs +10 -3
- package/dist/check-type-boundaries.mjs +67 -8
- package/dist/commands/dashboard-command.mjs +52 -14
- package/dist/commands/migration-command.mjs +18 -8
- package/dist/commands/module-command.mjs +142 -0
- package/dist/commands/preset-command.mjs +65 -4
- package/dist/commands/registry.mjs +483 -0
- package/dist/commands/task-command.mjs +111 -53
- package/dist/harness.mjs +6 -303
- package/dist/lib/capability-registry.mjs +229 -53
- package/dist/lib/check-module-parallel.mjs +1 -6
- package/dist/lib/check-profiles.mjs +39 -46
- package/dist/lib/check-task-contracts.mjs +6 -4
- package/dist/lib/command-registry.mjs +248 -0
- package/dist/lib/core-shared.mjs +78 -3
- package/dist/lib/dashboard-data.mjs +203 -22
- package/dist/lib/dashboard-workbench.mjs +245 -21
- package/dist/lib/dashboard-writer.mjs +4 -1
- package/dist/lib/git-status-summary.mjs +0 -1
- package/dist/lib/governance-index-generator.mjs +7 -5
- package/dist/lib/governance-sync.mjs +46 -121
- package/dist/lib/governance-table-boundary.mjs +1 -14
- package/dist/lib/harness-core.mjs +5 -1
- package/dist/lib/harness-paths.mjs +115 -1
- package/dist/lib/impact-classifier.mjs +420 -0
- package/dist/lib/lesson-maintenance.mjs +1 -2
- package/dist/lib/markdown-utils.mjs +50 -1
- package/dist/lib/migration-planner.mjs +31 -16
- package/dist/lib/migration-support.mjs +5 -4
- package/dist/lib/module-registry.mjs +296 -0
- package/dist/lib/preset-audit-contracts.mjs +24 -1
- package/dist/lib/preset-engine.mjs +68 -29
- package/dist/lib/preset-registry.mjs +374 -72
- package/dist/lib/preset-runner.mjs +560 -0
- package/dist/lib/review-confirm-git-gate.mjs +73 -19
- package/dist/lib/status-builder.mjs +23 -8
- package/dist/lib/structure-migration.mjs +6 -4
- package/dist/lib/subagent-authorization-audit.mjs +8 -2
- package/dist/lib/task-archive-eligibility.mjs +65 -0
- package/dist/lib/task-audit-metadata.mjs +25 -11
- package/dist/lib/task-audit-migration.mjs +21 -14
- package/dist/lib/task-discovery-contract.mjs +32 -0
- package/dist/lib/task-index.mjs +4 -2
- package/dist/lib/task-lesson-candidates.mjs +1 -2
- package/dist/lib/task-lesson-sedimentation.mjs +310 -9
- package/dist/lib/task-lifecycle/create-task-helpers.mjs +6 -3
- package/dist/lib/task-lifecycle/phase-sync.mjs +0 -1
- package/dist/lib/task-lifecycle/preset-interop.mjs +16 -0
- package/dist/lib/task-lifecycle/review-confirm.mjs +34 -2
- package/dist/lib/task-lifecycle/review-gates.mjs +12 -5
- package/dist/lib/task-lifecycle/review-submission.mjs +1 -2
- package/dist/lib/task-lifecycle/scaffold-provenance.mjs +0 -1
- package/dist/lib/task-lifecycle/template-files.mjs +2 -5
- package/dist/lib/task-lifecycle.mjs +117 -159
- package/dist/lib/task-metadata.mjs +10 -5
- package/dist/lib/task-preset-contract-drift.mjs +45 -0
- package/dist/lib/task-repository.mjs +192 -0
- package/dist/lib/task-review-model.mjs +38 -17
- package/dist/lib/task-scanner.mjs +75 -23
- package/dist/lib/task-template-materials.mjs +131 -0
- package/dist/lib/task-tombstone-commands.mjs +187 -18
- package/dist/lib/types/check-profiles.js +1 -0
- package/dist/lib/types/impact.js +1 -0
- package/dist/lib/types/preset.js +1 -0
- package/dist/lib/types/task-lifecycle.js +1 -0
- package/dist/lib/types/task-scanner.js +1 -0
- package/dist/postinstall.mjs +2 -2
- package/dist/run-built-tests.mjs +10 -3
- package/docs-release/README.md +2 -1
- package/docs-release/architecture/document-contract-kernel/README.md +150 -0
- package/docs-release/architecture/document-contract-kernel/products/full-skill-overlay.md +29 -0
- package/docs-release/architecture/document-contract-kernel/products/lite-forbidden-surfaces.txt +26 -0
- package/docs-release/architecture/document-contract-kernel/products/lite-skill-overlay.md +37 -0
- package/docs-release/architecture/overview.md +2 -2
- package/docs-release/architecture/overview.zh-CN.md +2 -2
- package/docs-release/architecture/system-explainer/01-system-overview.md +11 -7
- package/docs-release/architecture/system-explainer/02-module-dependency.md +4 -4
- package/docs-release/architecture/system-explainer/03-task-lifecycle.md +17 -12
- package/docs-release/architecture/system-explainer/05-data-flow.md +6 -6
- package/docs-release/architecture/system-explainer/06-preset-and-migration.md +2 -2
- package/docs-release/architecture/system-explainer/README.md +1 -1
- package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +12 -8
- package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +5 -5
- package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +19 -11
- package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
- package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +2 -2
- package/docs-release/architecture/system-explainer/en-US/README.md +1 -1
- package/docs-release/guides/agent-installation.en-US.md +4 -6
- package/docs-release/guides/agent-installation.md +11 -8
- package/docs-release/guides/contributing.md +10 -3
- package/docs-release/guides/contributing.zh-CN.md +10 -3
- package/docs-release/guides/legacy-migration-agent-prompt.md +1 -1
- package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +1 -1
- package/docs-release/guides/migration-playbook.en-US.md +9 -6
- package/docs-release/guides/migration-playbook.md +9 -6
- package/docs-release/guides/preset-development.md +68 -2
- package/docs-release/guides/task-state-machine.en-US.md +8 -8
- package/docs-release/guides/task-state-machine.md +7 -7
- package/docs-release/guides/typescript-runtime-migration-closeout.md +17 -13
- package/package.json +19 -11
- package/postinstall.mjs +37 -0
- 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/release-closeout/checks/check-release-package.mjs +29 -0
- package/presets/release-closeout/preset.yaml +100 -0
- package/presets/release-closeout/scripts/generate-release-package.mjs +572 -0
- package/presets/release-closeout/templates/execution_strategy.append.md +7 -0
- package/presets/release-closeout/templates/findings.seed.md +5 -0
- package/presets/release-closeout/templates/review.seed.md +3 -0
- package/presets/release-closeout/templates/task_plan.append.md +24 -0
- package/presets/standard-task/preset.yaml +2 -2
- package/references/agents-md-pattern.md +23 -17
- package/references/lessons-governance.md +2 -2
- package/references/module-parallel-standard.md +3 -6
- package/references/pull-request-standard.md +2 -2
- package/references/ssot-governance.md +2 -2
- package/references/taskr-gap-analysis.md +3 -3
- package/run-dist.mjs +34 -0
- package/skills/preset-creator/SKILL.md +40 -8
- package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -8
- package/skills/preset-creator/references/preset-package-skeleton.md +15 -5
- package/skills/preset-creator/references/structure-aware-paths.md +112 -0
- package/templates/AGENTS.md.template +28 -26
- package/templates/architecture/README.md +2 -2
- 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 +5 -1
- package/templates/dashboard/assets/app-src/10-router.js +7 -0
- package/templates/dashboard/assets/app-src/20-overview.js +8 -8
- package/templates/dashboard/assets/app-src/30-tasks.js +132 -40
- package/templates/dashboard/assets/app-src/32-task-swimlane.js +314 -0
- package/templates/dashboard/assets/app-src/35-task-detail.js +35 -5
- package/templates/dashboard/assets/app-src/40-modules.js +257 -41
- package/templates/dashboard/assets/app-src/45-review.js +127 -1
- package/templates/dashboard/assets/app-src/90-bindings.js +185 -2
- package/templates/dashboard/assets/app.css +928 -53
- package/templates/dashboard/assets/app.css.manifest.json +2 -0
- package/templates/dashboard/assets/app.js +1071 -98
- package/templates/dashboard/assets/app.manifest.json +1 -0
- package/templates/dashboard/assets/css-src/00-foundation.css +12 -6
- package/templates/dashboard/assets/css-src/10-panels-flow.css +2 -2
- package/templates/dashboard/assets/css-src/30-task-index.css +21 -13
- package/templates/dashboard/assets/css-src/31-archive.css +94 -0
- package/templates/dashboard/assets/css-src/32-task-swimlane.css +487 -0
- package/templates/dashboard/assets/css-src/35-review-workspace.css +78 -0
- package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +191 -14
- package/templates/dashboard/assets/css-src/50-responsive-overrides.css +23 -0
- package/templates/dashboard/assets/i18n.js +166 -2
- package/templates/development/README.md +9 -9
- package/templates/development/cross-repo-debugging.md +3 -3
- package/templates/development/external-context/service-template.md +1 -1
- package/templates/development/external-source-packs/README.md +2 -2
- package/templates/integrations/README.md +4 -4
- package/templates/integrations/api-contract.md +1 -1
- package/templates/integrations/event-contract.md +1 -1
- package/templates/integrations/third-party/vendor-template.md +1 -1
- package/templates/integrations/webhook-contract.md +1 -1
- package/templates/ledger/Harness-Ledger.md +1 -1
- package/templates/modules/module_brief.md +50 -0
- package/templates/modules/module_plan.md +49 -0
- package/templates/modules/registry_view.md +9 -0
- package/templates/modules/session_prompt_pack.md +55 -0
- package/templates/planning/brief.md +32 -8
- package/templates/planning/module_brief.md +28 -3
- package/templates/planning/module_plan.md +26 -11
- package/templates/planning/module_session_prompt.md +11 -2
- package/templates/planning/optional/slices/_slice-template/brief.md +28 -0
- package/templates/planning/review.md +1 -1
- package/templates/planning/visual_map.md +1 -1
- package/templates/reference/docs-library-standard.md +7 -7
- package/templates/reference/execution-workflow-standard.md +13 -0
- package/templates/reference/external-source-intake-standard.md +10 -10
- package/templates/reference/pull-request-standard.md +2 -2
- package/templates/reference/repo-governance-standard.md +1 -1
- package/templates/reference/review-routing-standard.md +4 -0
- package/templates/ssot/Module-Registry.md +4 -38
- package/templates/walkthrough/walkthrough-template.md +1 -1
- package/templates-zh-CN/AGENTS.md.template +27 -25
- package/templates-zh-CN/CLAUDE.md.template +1 -1
- package/templates-zh-CN/architecture/README.md +2 -2
- 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 +9 -9
- package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
- package/templates-zh-CN/development/external-context/service-template.md +1 -1
- package/templates-zh-CN/development/external-source-packs/README.md +2 -2
- package/templates-zh-CN/integrations/README.md +4 -4
- package/templates-zh-CN/integrations/api-contract.md +1 -1
- package/templates-zh-CN/integrations/event-contract.md +1 -1
- package/templates-zh-CN/integrations/third-party/vendor-template.md +1 -1
- package/templates-zh-CN/integrations/webhook-contract.md +1 -1
- 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/modules/module_brief.md +47 -0
- package/templates-zh-CN/modules/module_plan.md +48 -0
- package/templates-zh-CN/modules/registry_view.md +9 -0
- package/templates-zh-CN/modules/session_prompt_pack.md +50 -0
- package/templates-zh-CN/planning/INDEX.md +1 -0
- package/templates-zh-CN/planning/brief.md +26 -7
- package/templates-zh-CN/planning/module_brief.md +24 -2
- package/templates-zh-CN/planning/module_plan.md +35 -29
- package/templates-zh-CN/planning/module_session_prompt.md +15 -11
- package/templates-zh-CN/planning/optional/slices/_slice-template/brief.md +28 -11
- package/templates-zh-CN/planning/review.md +1 -1
- package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
- package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
- package/templates-zh-CN/reference/docs-library-standard.md +27 -27
- package/templates-zh-CN/reference/execution-workflow-standard.md +12 -2
- package/templates-zh-CN/reference/external-source-intake-standard.md +10 -10
- package/templates-zh-CN/reference/harness-ledger-standard.md +3 -3
- package/templates-zh-CN/reference/pull-request-standard.md +1 -1
- 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 +3 -0
- package/templates-zh-CN/reference/walkthrough-standard.md +2 -2
- 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 +2 -2
- package/templates-zh-CN/ssot/Module-Registry.md +5 -44
- package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
- package/templates-zh-CN/walkthrough/walkthrough-template.md +4 -4
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { repoRoot, visualMapFile, normalizeTarget, toPosix, exists, existsInDocs, readFileSafe, readJsonSafe, readBundledTemplate, walkFiles, normalizeLocale, localizedTemplateSource, userPresetRootForHome, } from "./core-shared.mjs";
|
|
6
|
+
import { repoRoot, visualMapFile, normalizeTarget, toPosix, exists, existsInDocs, readFileSafe, readJsonSafe, readBundledTemplate, renderHarnessTemplate, walkFiles, normalizeLocale, localizedTemplateSource, userPresetRootForHome, } from "./core-shared.mjs";
|
|
7
7
|
import { listBundledPresetIds, seedBundledPresets } from "./preset-registry.mjs";
|
|
8
|
-
import { legacyCloseoutFile, legacyCompatMode, legacyLedgerFile, legacyModuleRoot, legacyPath, legacyPlanningRoot, legacyTaskRoot, legacyWalkthroughRoot, safeAdoptionCapability, v2HarnessRoot, } from "./harness-paths.mjs";
|
|
8
|
+
import { legacyCloseoutFile, legacyCompatMode, legacyLedgerFile, legacyModuleRoot, legacyPath, legacyPlanningRoot, legacyTaskRoot, legacyWalkthroughRoot, safeAdoptionCapability, assertRenderableHarnessManifest, renderHarnessManifest, v2HarnessRoot, } from "./harness-paths.mjs";
|
|
9
|
+
function errorMessage(error) {
|
|
10
|
+
return error instanceof Error ? error.message : String(error || "unknown error");
|
|
11
|
+
}
|
|
9
12
|
export const capabilityDefinitions = {
|
|
10
13
|
core: {
|
|
11
14
|
description: "Planning loop and task execution records.",
|
|
@@ -15,7 +18,7 @@ export const capabilityDefinitions = {
|
|
|
15
18
|
artifacts: [legacyPath(legacyPlanningRoot)],
|
|
16
19
|
},
|
|
17
20
|
"module-parallel": {
|
|
18
|
-
description: "
|
|
21
|
+
description: "YAML-backed module registry, module briefs/plans, and global worker handoff prompt pack.",
|
|
19
22
|
selectWhen: "Use only when the project has two or more independent modules that need parallel ownership.",
|
|
20
23
|
default: false,
|
|
21
24
|
dependencies: ["core"],
|
|
@@ -72,7 +75,7 @@ export function readCapabilityRegistry(target) {
|
|
|
72
75
|
if (target.harness?.version === 2 && target.harness.manifest) {
|
|
73
76
|
return {
|
|
74
77
|
mode: "v2-manifest",
|
|
75
|
-
path: target.harness.manifestPath,
|
|
78
|
+
path: target.harness.manifestPath || target.manifestPath || "",
|
|
76
79
|
capabilities: (target.harness.manifest.capabilities || ["core"]).map((name) => ({
|
|
77
80
|
name: normalizeCapabilityName(name),
|
|
78
81
|
state: "configured",
|
|
@@ -96,18 +99,19 @@ export function readCapabilityRegistry(target) {
|
|
|
96
99
|
let readError = null;
|
|
97
100
|
const raw = readJsonSafe(registryPath, null, { onError: (error) => { readError = error; } });
|
|
98
101
|
if (raw) {
|
|
99
|
-
const locale = normalizeLocale(raw.locale);
|
|
102
|
+
const locale = normalizeLocale(String(raw.locale || ""));
|
|
100
103
|
const capabilities = Array.isArray(raw.capabilities)
|
|
101
104
|
? raw.capabilities.map((entry) => typeof entry === "string"
|
|
102
105
|
? { name: normalizeCapabilityName(entry), state: "scaffolded" }
|
|
103
|
-
: { name: normalizeCapabilityName(entry.name), state: entry.state || "scaffolded" })
|
|
106
|
+
: { name: normalizeCapabilityName(entry.name), state: String(entry.state || "scaffolded") })
|
|
104
107
|
: [];
|
|
105
108
|
return { mode: "declared-capability", path: registryPath, capabilities, raw, locale, errors: [] };
|
|
106
109
|
}
|
|
107
|
-
return { mode: "declared-capability", path: registryPath, capabilities: [], raw: null, errors: [readError
|
|
110
|
+
return { mode: "declared-capability", path: registryPath, capabilities: [], locale: "en-US", raw: null, errors: [errorMessage(readError) || "invalid .harness-capabilities.json"] };
|
|
108
111
|
}
|
|
109
112
|
export function normalizeCapabilityName(name) {
|
|
110
|
-
|
|
113
|
+
const normalized = String(name || "");
|
|
114
|
+
return capabilityAliases[normalized] || normalized;
|
|
111
115
|
}
|
|
112
116
|
export function validateSourcePackageBoundary(targetInput = ".") {
|
|
113
117
|
const root = path.resolve(targetInput || ".");
|
|
@@ -165,17 +169,17 @@ function validateDashboardAssetAssembly(root, manifestName, assetName, driftMess
|
|
|
165
169
|
return trackedAsset === assembled ? [] : [driftMessage];
|
|
166
170
|
}
|
|
167
171
|
catch (error) {
|
|
168
|
-
return [`could not validate dashboard asset assembly (${assetName}): ${error
|
|
172
|
+
return [`could not validate dashboard asset assembly (${assetName}): ${errorMessage(error)}`];
|
|
169
173
|
}
|
|
170
174
|
}
|
|
171
175
|
export function detectCapabilities(target) {
|
|
172
176
|
const detected = new Set(["core"]);
|
|
173
177
|
if (target.harness?.version === 2) {
|
|
174
|
-
if (fs.existsSync(path.join(target.harness.modulesRoot, "Module-Registry.md")))
|
|
178
|
+
if (fs.existsSync(path.join(target.harness.modulesRoot || "", "Module-Registry.md")))
|
|
175
179
|
detected.add("module-parallel");
|
|
176
|
-
if (fs.existsSync(path.join(target.harness.governanceRoot, "standards/adversarial-review-standard.md")))
|
|
180
|
+
if (fs.existsSync(path.join(target.harness.governanceRoot || "", "standards/adversarial-review-standard.md")))
|
|
177
181
|
detected.add("adversarial-review");
|
|
178
|
-
if (fs.existsSync(path.join(target.harness.tasksRoot, "_task-template/long-running-task-contract.md")))
|
|
182
|
+
if (fs.existsSync(path.join(target.harness.tasksRoot || "", "_task-template/long-running-task-contract.md")))
|
|
179
183
|
detected.add("long-running-task");
|
|
180
184
|
return [...detected];
|
|
181
185
|
}
|
|
@@ -224,7 +228,7 @@ export function buildInstallReport({ target, locale, capabilities, changes, dryR
|
|
|
224
228
|
function packageVersion() {
|
|
225
229
|
try {
|
|
226
230
|
const pkg = readJsonSafe(path.join(repoRoot, "package.json"), {});
|
|
227
|
-
return pkg.version || "";
|
|
231
|
+
return String(pkg.version || "");
|
|
228
232
|
}
|
|
229
233
|
catch {
|
|
230
234
|
return "";
|
|
@@ -319,7 +323,7 @@ export function installUserSkill({ agent = "codex", home = "", dryRun = false, f
|
|
|
319
323
|
function readInstalledVersion(targetRoot) {
|
|
320
324
|
try {
|
|
321
325
|
const pkg = readJsonSafe(path.join(targetRoot, "package.json"), {});
|
|
322
|
-
return pkg.version || "";
|
|
326
|
+
return String(pkg.version || "");
|
|
323
327
|
}
|
|
324
328
|
catch {
|
|
325
329
|
return "";
|
|
@@ -422,13 +426,13 @@ function capabilityArtifactsForTarget(target, capabilityName) {
|
|
|
422
426
|
const paths = target.harness;
|
|
423
427
|
switch (capabilityName) {
|
|
424
428
|
case "core":
|
|
425
|
-
return [relative(paths.planningRoot)];
|
|
429
|
+
return [relative(paths.planningRoot || "")];
|
|
426
430
|
case "module-parallel":
|
|
427
|
-
return [relative(path.join(paths.modulesRoot, "Module-Registry.md")), relative(paths.modulesRoot)];
|
|
431
|
+
return [relative(path.join(paths.modulesRoot || "", "Module-Registry.md")), relative(paths.modulesRoot || "")];
|
|
428
432
|
case "subagent-worker":
|
|
429
|
-
return [relative(paths.modulesRoot)];
|
|
433
|
+
return [relative(paths.modulesRoot || "")];
|
|
430
434
|
case "adversarial-review":
|
|
431
|
-
return [relative(paths.tasksRoot)];
|
|
435
|
+
return [relative(paths.tasksRoot || "")];
|
|
432
436
|
case "long-running-task":
|
|
433
437
|
return [];
|
|
434
438
|
default:
|
|
@@ -436,11 +440,11 @@ function capabilityArtifactsForTarget(target, capabilityName) {
|
|
|
436
440
|
}
|
|
437
441
|
}
|
|
438
442
|
export function plannedInitFiles(capabilities = ["core"], { locale = "en-US", paths = null } = {}) {
|
|
439
|
-
const root = paths ? toPosix(path.relative(paths.projectRoot, paths.harnessRoot)) : v2HarnessRoot;
|
|
440
|
-
const modulesRoot = paths ? toPosix(path.relative(paths.projectRoot, paths.modulesRoot)) : `${root}/planning/modules`;
|
|
441
|
-
const regressionRoot = paths ? toPosix(path.relative(paths.projectRoot, paths.regressionRoot)) : `${root}/governance/regression`;
|
|
443
|
+
const root = paths ? toPosix(path.relative(paths.projectRoot || "", paths.harnessRoot || "")) : v2HarnessRoot;
|
|
444
|
+
const modulesRoot = paths ? toPosix(path.relative(paths.projectRoot || "", paths.modulesRoot || "")) : `${root}/planning/modules`;
|
|
445
|
+
const regressionRoot = paths ? toPosix(path.relative(paths.projectRoot || "", paths.regressionRoot || "")) : `${root}/governance/regression`;
|
|
442
446
|
const contextRoot = `${root}/context`;
|
|
443
|
-
const governanceRoot = paths ? toPosix(path.relative(paths.projectRoot, paths.governanceRoot)) : `${root}/governance`;
|
|
447
|
+
const governanceRoot = paths ? toPosix(path.relative(paths.projectRoot || "", paths.governanceRoot || "")) : `${root}/governance`;
|
|
444
448
|
const files = [
|
|
445
449
|
["AGENTS.md", "templates/AGENTS.md.template"],
|
|
446
450
|
["CLAUDE.md", "templates/CLAUDE.md.template"],
|
|
@@ -470,16 +474,16 @@ export function plannedInitFiles(capabilities = ["core"], { locale = "en-US", pa
|
|
|
470
474
|
[`${governanceRoot}/standards/external-source-intake-standard.md`, "templates/reference/external-source-intake-standard.md"],
|
|
471
475
|
];
|
|
472
476
|
if (capabilities.includes("module-parallel")) {
|
|
473
|
-
files.push([`${modulesRoot}/Module-Registry.md`, "templates/
|
|
474
|
-
files.push([`${modulesRoot}/Session-Prompt-Pack.md`, "templates/
|
|
477
|
+
files.push([`${modulesRoot}/Module-Registry.md`, "templates/modules/registry_view.md"]);
|
|
478
|
+
files.push([`${modulesRoot}/Session-Prompt-Pack.md`, "templates/modules/session_prompt_pack.md"]);
|
|
475
479
|
}
|
|
476
480
|
return files.map(([destination, source]) => [destination, localizedTemplateSource(source, locale)]);
|
|
477
481
|
}
|
|
478
482
|
function plannedInitDirectories(capabilities = ["core"], { paths = null } = {}) {
|
|
479
|
-
const planningRoot = paths ? toPosix(path.relative(paths.projectRoot, paths.planningRoot)) : `${v2HarnessRoot}/planning`;
|
|
480
|
-
const tasksRoot = paths ? toPosix(path.relative(paths.projectRoot, paths.tasksRoot)) : `${v2HarnessRoot}/planning/tasks`;
|
|
481
|
-
const modulesRoot = paths ? toPosix(path.relative(paths.projectRoot, paths.modulesRoot)) : `${v2HarnessRoot}/planning/modules`;
|
|
482
|
-
const generatedRoot = paths ? toPosix(path.relative(paths.projectRoot, paths.generatedRoot)) : `${v2HarnessRoot}/governance/generated`;
|
|
483
|
+
const planningRoot = paths ? toPosix(path.relative(paths.projectRoot || "", paths.planningRoot || "")) : `${v2HarnessRoot}/planning`;
|
|
484
|
+
const tasksRoot = paths ? toPosix(path.relative(paths.projectRoot || "", paths.tasksRoot || "")) : `${v2HarnessRoot}/planning/tasks`;
|
|
485
|
+
const modulesRoot = paths ? toPosix(path.relative(paths.projectRoot || "", paths.modulesRoot || "")) : `${v2HarnessRoot}/planning/modules`;
|
|
486
|
+
const generatedRoot = paths ? toPosix(path.relative(paths.projectRoot || "", paths.generatedRoot || "")) : `${v2HarnessRoot}/governance/generated`;
|
|
483
487
|
const directories = [
|
|
484
488
|
planningRoot,
|
|
485
489
|
tasksRoot,
|
|
@@ -505,6 +509,7 @@ export function writeInitFiles(targetInput, capabilities, { dryRun = true, local
|
|
|
505
509
|
}
|
|
506
510
|
const planned = plannedInitFiles(normalizedCapabilities, { locale: normalizedLocale });
|
|
507
511
|
const changes = [];
|
|
512
|
+
const projectionEntries = [];
|
|
508
513
|
const manifestDestination = `${v2HarnessRoot}/harness.yaml`;
|
|
509
514
|
const manifestPath = path.join(target.projectRoot, manifestDestination);
|
|
510
515
|
const manifestExists = fs.existsSync(manifestPath);
|
|
@@ -528,9 +533,13 @@ export function writeInitFiles(targetInput, capabilities, { dryRun = true, local
|
|
|
528
533
|
changes.push({ destination, source, action: existsAlready ? "skip-existing" : dryRun ? "would-create" : "create" });
|
|
529
534
|
if (!dryRun && !existsAlready) {
|
|
530
535
|
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
531
|
-
|
|
536
|
+
const rendered = renderInstallTemplate(source, target);
|
|
537
|
+
fs.writeFileSync(destinationPath, rendered);
|
|
538
|
+
projectionEntries.push(templateProjectionEntry({ destination, source, target, rendered }));
|
|
532
539
|
}
|
|
533
540
|
}
|
|
541
|
+
if (!dryRun)
|
|
542
|
+
writeTemplateProjectionManifest(target, projectionEntries);
|
|
534
543
|
if (addNpmScripts) {
|
|
535
544
|
changes.push(...writeNpmScripts(target, { dryRun }));
|
|
536
545
|
}
|
|
@@ -538,26 +547,6 @@ export function writeInitFiles(targetInput, capabilities, { dryRun = true, local
|
|
|
538
547
|
const report = buildInstallReport({ target, locale: normalizedLocale, capabilities: normalizedCapabilities, changes, dryRun, operation: "init" });
|
|
539
548
|
return { target, capabilities: normalizedCapabilities, locale: normalizedLocale, changes, presetSeed, nextCommands: initNextCommands(), report };
|
|
540
549
|
}
|
|
541
|
-
function renderHarnessManifest({ locale, capabilities, structure = null }) {
|
|
542
|
-
const manifestStructure = structure || {
|
|
543
|
-
harnessRoot: v2HarnessRoot,
|
|
544
|
-
planningRoot: `${v2HarnessRoot}/planning`,
|
|
545
|
-
tasksRoot: `${v2HarnessRoot}/planning/tasks`,
|
|
546
|
-
modulesRoot: `${v2HarnessRoot}/planning/modules`,
|
|
547
|
-
externalRoot: `${v2HarnessRoot}/planning/external`,
|
|
548
|
-
governanceRoot: `${v2HarnessRoot}/governance`,
|
|
549
|
-
generatedRoot: `${v2HarnessRoot}/governance/generated`,
|
|
550
|
-
};
|
|
551
|
-
return [
|
|
552
|
-
"version: 2",
|
|
553
|
-
`locale: ${locale}`,
|
|
554
|
-
"capabilities:",
|
|
555
|
-
...capabilities.map((capability) => ` - ${capability}`),
|
|
556
|
-
"structure:",
|
|
557
|
-
...Object.entries(manifestStructure).map(([key, value]) => ` ${key}: ${value}`),
|
|
558
|
-
"",
|
|
559
|
-
].join("\n");
|
|
560
|
-
}
|
|
561
550
|
function initNextCommands() {
|
|
562
551
|
return [
|
|
563
552
|
"npx --yes coding-agent-harness dev .",
|
|
@@ -591,6 +580,102 @@ function writeNpmScripts(target, { dryRun = true } = {}) {
|
|
|
591
580
|
}
|
|
592
581
|
return [{ destination: "package.json", source: "npm-scripts", action: dryRun ? "would-update-scripts" : "update-scripts" }, ...scriptChanges];
|
|
593
582
|
}
|
|
583
|
+
function renderInstallTemplate(source, target) {
|
|
584
|
+
return renderHarnessTemplate(readBundledTemplate(source), { paths: targetPathContext(target) });
|
|
585
|
+
}
|
|
586
|
+
function targetPathContext(target) {
|
|
587
|
+
const paths = target.harness || {};
|
|
588
|
+
const projectRoot = String(paths.projectRoot || target.projectRoot);
|
|
589
|
+
const fields = [
|
|
590
|
+
"harnessRoot",
|
|
591
|
+
"planningRoot",
|
|
592
|
+
"tasksRoot",
|
|
593
|
+
"modulesRoot",
|
|
594
|
+
"externalRoot",
|
|
595
|
+
"governanceRoot",
|
|
596
|
+
"generatedRoot",
|
|
597
|
+
"regressionRoot",
|
|
598
|
+
"ledgerPath",
|
|
599
|
+
"closeoutIndexPath",
|
|
600
|
+
];
|
|
601
|
+
return Object.fromEntries(fields.map((field) => {
|
|
602
|
+
const value = String(paths[field] || "");
|
|
603
|
+
const rendered = value && path.isAbsolute(value) ? toPosix(path.relative(projectRoot, value)) : toPosix(String(value || ""));
|
|
604
|
+
return [field, rendered];
|
|
605
|
+
}));
|
|
606
|
+
}
|
|
607
|
+
function templateProjectionManifestPath(target) {
|
|
608
|
+
const effectiveTarget = target.harness?.version === 2 || !fs.existsSync(path.join(target.projectRoot, v2HarnessRoot, "harness.yaml"))
|
|
609
|
+
? target
|
|
610
|
+
: normalizeTarget(target.projectRoot);
|
|
611
|
+
const generatedRoot = effectiveTarget.harness?.generatedRoot || path.join(effectiveTarget.projectRoot, v2HarnessRoot, "governance/generated");
|
|
612
|
+
return path.join(generatedRoot, "Template-Projections.json");
|
|
613
|
+
}
|
|
614
|
+
function readTemplateProjectionManifest(target) {
|
|
615
|
+
const currentPath = templateProjectionManifestPath(target);
|
|
616
|
+
if (fs.existsSync(currentPath))
|
|
617
|
+
return readJsonSafe(currentPath, { schemaVersion: "template-projections/v1", entries: [] });
|
|
618
|
+
const fallback = walkFiles(target.projectRoot)
|
|
619
|
+
.find((filePath) => path.basename(filePath) === "Template-Projections.json");
|
|
620
|
+
return fallback ? readJsonSafe(fallback, { schemaVersion: "template-projections/v1", entries: [] }) : { schemaVersion: "template-projections/v1", entries: [] };
|
|
621
|
+
}
|
|
622
|
+
function writeTemplateProjectionManifest(target, entries) {
|
|
623
|
+
if (!entries.length)
|
|
624
|
+
return;
|
|
625
|
+
const manifestPath = templateProjectionManifestPath(target);
|
|
626
|
+
const current = readTemplateProjectionManifest(target);
|
|
627
|
+
const byDestination = new Map((current.entries || []).map((entry) => [entry.destination, entry]));
|
|
628
|
+
for (const entry of entries)
|
|
629
|
+
byDestination.set(entry.destination, entry);
|
|
630
|
+
const next = {
|
|
631
|
+
schemaVersion: "template-projections/v1",
|
|
632
|
+
generatedAt: new Date().toISOString(),
|
|
633
|
+
entries: [...byDestination.values()].sort((a, b) => a.destination.localeCompare(b.destination)),
|
|
634
|
+
};
|
|
635
|
+
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
636
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(next, null, 2)}\n`);
|
|
637
|
+
}
|
|
638
|
+
function templateProjectionEntry({ destination, source, target, rendered }) {
|
|
639
|
+
return {
|
|
640
|
+
destination,
|
|
641
|
+
source,
|
|
642
|
+
ownership: "package-template-pristine",
|
|
643
|
+
renderedSha256: sha256(rendered),
|
|
644
|
+
sourceSha256: sha256(readBundledTemplate(source)),
|
|
645
|
+
paths: targetPathContext(target),
|
|
646
|
+
updatedAt: new Date().toISOString(),
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
function pathFindings(content, target) {
|
|
650
|
+
const findings = [];
|
|
651
|
+
const text = String(content || "");
|
|
652
|
+
if (text.includes("{{paths."))
|
|
653
|
+
findings.push("unresolved-path-token");
|
|
654
|
+
const defaultRootPattern = /\bcoding-agent-harness\/(?:planning|governance|context)\//g;
|
|
655
|
+
if (target.harness?.version === 2 && targetPathContext(target).harnessRoot !== v2HarnessRoot && defaultRootPattern.test(text)) {
|
|
656
|
+
findings.push("default-root-literal");
|
|
657
|
+
}
|
|
658
|
+
if (/docs\/09-PLANNING|docs\/10-WALKTHROUGH|docs\/Harness-Ledger\.md/.test(text))
|
|
659
|
+
findings.push("legacy-path-literal");
|
|
660
|
+
return [...new Set(findings)];
|
|
661
|
+
}
|
|
662
|
+
function scanProjectAuthoredPathFindings(target, plannedDestinations) {
|
|
663
|
+
const root = target.harness?.harnessRoot || target.docsRoot;
|
|
664
|
+
if (!root || !fs.existsSync(root))
|
|
665
|
+
return [];
|
|
666
|
+
return walkFiles(root)
|
|
667
|
+
.map((filePath) => toPosix(path.relative(target.projectRoot, filePath)))
|
|
668
|
+
.filter((relative) => !plannedDestinations.has(relative))
|
|
669
|
+
.filter((relative) => !relative.includes("/governance/archive/") && !relative.includes("/planning/tasks/") && !relative.includes("/governance/generated/"))
|
|
670
|
+
.map((relative) => {
|
|
671
|
+
const findings = pathFindings(readFileSafe(path.join(target.projectRoot, relative)), target);
|
|
672
|
+
return findings.length ? { destination: relative, ownership: "project-authored", action: "report-only", pathFindings: findings } : null;
|
|
673
|
+
})
|
|
674
|
+
.filter((change) => Boolean(change));
|
|
675
|
+
}
|
|
676
|
+
function sha256(content) {
|
|
677
|
+
return crypto.createHash("sha256").update(String(content)).digest("hex");
|
|
678
|
+
}
|
|
594
679
|
export function addCapability(targetInput, capabilityName, { dryRun = true, locale = "" } = {}) {
|
|
595
680
|
const target = normalizeTarget(targetInput);
|
|
596
681
|
const normalizedCapability = normalizeCapabilityName(capabilityName);
|
|
@@ -608,6 +693,7 @@ export function addCapability(targetInput, capabilityName, { dryRun = true, loca
|
|
|
608
693
|
const nextCapabilities = [...capabilityMap.keys()];
|
|
609
694
|
const scaffold = plannedInitFiles([...capabilityMap.keys()], { locale: normalizedLocale, paths: target.harness?.version === 2 ? target.harness : null });
|
|
610
695
|
const changes = [];
|
|
696
|
+
const projectionEntries = [];
|
|
611
697
|
for (const directory of plannedInitDirectories(nextCapabilities, { paths: target.harness?.version === 2 ? target.harness : null })) {
|
|
612
698
|
const destinationPath = path.join(target.projectRoot, directory);
|
|
613
699
|
const existsAlready = fs.existsSync(destinationPath);
|
|
@@ -622,15 +708,19 @@ export function addCapability(targetInput, capabilityName, { dryRun = true, loca
|
|
|
622
708
|
changes.push({ destination, source, action: existsAlready ? "skip-existing" : dryRun ? "would-create" : "create" });
|
|
623
709
|
if (!dryRun && !existsAlready) {
|
|
624
710
|
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
625
|
-
|
|
711
|
+
const rendered = renderInstallTemplate(source, target);
|
|
712
|
+
fs.writeFileSync(destinationPath, rendered);
|
|
713
|
+
projectionEntries.push(templateProjectionEntry({ destination, source, target, rendered }));
|
|
626
714
|
}
|
|
627
715
|
}
|
|
628
716
|
if (!dryRun) {
|
|
629
|
-
const manifestPath = target.harness
|
|
717
|
+
const manifestPath = target.harness?.version === 2 && target.manifestPath
|
|
630
718
|
? target.manifestPath
|
|
631
719
|
: path.join(target.projectRoot, v2HarnessRoot, "harness.yaml");
|
|
720
|
+
assertRenderableHarnessManifest(target.harness?.manifest);
|
|
632
721
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
633
|
-
fs.writeFileSync(manifestPath, renderHarnessManifest({ locale: normalizedLocale, capabilities: nextCapabilities, structure: target.harness
|
|
722
|
+
fs.writeFileSync(manifestPath, renderHarnessManifest({ locale: normalizedLocale, capabilities: nextCapabilities, structure: target.harness?.manifest?.structure, modules: target.harness?.manifest?.modules || null }));
|
|
723
|
+
writeTemplateProjectionManifest(target, projectionEntries);
|
|
634
724
|
}
|
|
635
725
|
const report = buildInstallReport({ target, locale: normalizedLocale, capabilities: [...capabilityMap.keys()], changes, dryRun, operation: "add-capability" });
|
|
636
726
|
return {
|
|
@@ -641,3 +731,89 @@ export function addCapability(targetInput, capabilityName, { dryRun = true, loca
|
|
|
641
731
|
report,
|
|
642
732
|
};
|
|
643
733
|
}
|
|
734
|
+
export function auditTemplateProjections(targetInput = ".") {
|
|
735
|
+
const target = normalizeTarget(targetInput);
|
|
736
|
+
const registry = readCapabilityRegistry(target);
|
|
737
|
+
const capabilities = registry.capabilities.map((capability) => capability.name);
|
|
738
|
+
const planned = plannedInitFiles(capabilities, { locale: registry.locale, paths: target.harness?.version === 2 ? target.harness : null });
|
|
739
|
+
const manifest = readTemplateProjectionManifest(target);
|
|
740
|
+
const entriesByDestination = new Map((manifest.entries || []).map((entry) => [entry.destination, entry]));
|
|
741
|
+
const plannedDestinations = new Set(planned.map(([destination]) => destination));
|
|
742
|
+
const projections = planned.map(([destination, source]) => {
|
|
743
|
+
const destinationPath = path.join(target.projectRoot, destination);
|
|
744
|
+
const rendered = renderInstallTemplate(source, target);
|
|
745
|
+
const renderedSha256 = sha256(rendered);
|
|
746
|
+
const existsAlready = fs.existsSync(destinationPath);
|
|
747
|
+
const current = existsAlready ? fs.readFileSync(destinationPath, "utf8") : "";
|
|
748
|
+
const currentSha256 = existsAlready ? sha256(current) : "";
|
|
749
|
+
const recorded = entriesByDestination.get(destination);
|
|
750
|
+
const ownership = !existsAlready
|
|
751
|
+
? "missing"
|
|
752
|
+
: currentSha256 === renderedSha256
|
|
753
|
+
? "package-template-pristine"
|
|
754
|
+
: recorded && currentSha256 === recorded.renderedSha256
|
|
755
|
+
? "package-template-pristine"
|
|
756
|
+
: recorded
|
|
757
|
+
? "package-template-modified"
|
|
758
|
+
: "project-authored";
|
|
759
|
+
const action = !existsAlready
|
|
760
|
+
? "would-create"
|
|
761
|
+
: currentSha256 === renderedSha256
|
|
762
|
+
? "no-op"
|
|
763
|
+
: ownership === "package-template-pristine"
|
|
764
|
+
? "would-refresh"
|
|
765
|
+
: "report-only";
|
|
766
|
+
return {
|
|
767
|
+
destination,
|
|
768
|
+
source,
|
|
769
|
+
exists: existsAlready,
|
|
770
|
+
ownership,
|
|
771
|
+
action,
|
|
772
|
+
currentSha256: currentSha256 || null,
|
|
773
|
+
expectedSha256: renderedSha256,
|
|
774
|
+
recordedSha256: recorded?.renderedSha256 || null,
|
|
775
|
+
pathFindings: pathFindings(current, target),
|
|
776
|
+
};
|
|
777
|
+
});
|
|
778
|
+
const authoredFindings = scanProjectAuthoredPathFindings(target, plannedDestinations);
|
|
779
|
+
return {
|
|
780
|
+
operation: "template-projection-audit",
|
|
781
|
+
target: target.projectRoot,
|
|
782
|
+
manifestPath: templateProjectionManifestPath(target),
|
|
783
|
+
projections,
|
|
784
|
+
projectAuthoredFindings: authoredFindings,
|
|
785
|
+
summary: {
|
|
786
|
+
total: projections.length,
|
|
787
|
+
missing: projections.filter((item) => item.ownership === "missing").length,
|
|
788
|
+
refreshable: projections.filter((item) => item.action === "would-refresh").length,
|
|
789
|
+
reportOnly: projections.filter((item) => item.action === "report-only").length + authoredFindings.length,
|
|
790
|
+
},
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
export function refreshTemplateProjections(targetInput = ".", { apply = false } = {}) {
|
|
794
|
+
const target = normalizeTarget(targetInput);
|
|
795
|
+
const audit = auditTemplateProjections(targetInput);
|
|
796
|
+
const changes = [];
|
|
797
|
+
const entries = [];
|
|
798
|
+
for (const item of audit.projections) {
|
|
799
|
+
if (!["would-create", "would-refresh"].includes(item.action))
|
|
800
|
+
continue;
|
|
801
|
+
const rendered = renderInstallTemplate(item.source, target);
|
|
802
|
+
changes.push({ destination: item.destination, source: item.source, action: apply ? item.action.replace("would-", "") : item.action });
|
|
803
|
+
entries.push(templateProjectionEntry({ destination: item.destination, source: item.source, target, rendered }));
|
|
804
|
+
if (!apply)
|
|
805
|
+
continue;
|
|
806
|
+
const destinationPath = path.join(target.projectRoot, item.destination);
|
|
807
|
+
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
808
|
+
fs.writeFileSync(destinationPath, rendered);
|
|
809
|
+
}
|
|
810
|
+
if (apply)
|
|
811
|
+
writeTemplateProjectionManifest(target, entries);
|
|
812
|
+
return {
|
|
813
|
+
operation: "template-projection-refresh",
|
|
814
|
+
dryRun: !apply,
|
|
815
|
+
target: target.projectRoot,
|
|
816
|
+
changes,
|
|
817
|
+
audit,
|
|
818
|
+
};
|
|
819
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
// Module-parallel private harness checks stay behavior-first until legacy module context types are modeled.
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
@@ -197,12 +196,8 @@ export function checkModuleParallelStructure(context) {
|
|
|
197
196
|
fail(`${moduleRegistryPath} row ${key} branch must use codex/ prefix: ${branch}`);
|
|
198
197
|
}
|
|
199
198
|
const block = modulePromptBlock(promptPack, key);
|
|
200
|
-
if (!block)
|
|
201
|
-
if (!exists(legacyPath(legacyModuleRoot, key, "session_prompt.md"))) {
|
|
202
|
-
fail(`missing module session prompt for ${key}`);
|
|
203
|
-
}
|
|
199
|
+
if (!block)
|
|
204
200
|
continue;
|
|
205
|
-
}
|
|
206
201
|
for (const term of [
|
|
207
202
|
"Current Step",
|
|
208
203
|
branchName,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { spawnSync } from "node:child_process";
|
|
@@ -6,9 +5,9 @@ import { repoRoot, bundledCheckScript, visualMapFile, legacyVisualRoadmapFile, a
|
|
|
6
5
|
import { tableAfterHeading, getColumn, getColumnAny, splitList, firstColumn, contentHasAny, } from "./markdown-utils.mjs";
|
|
7
6
|
import { validateCapabilities } from "./capability-registry.mjs";
|
|
8
7
|
import { readPresetPackage } from "./preset-registry.mjs";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { validateRegularTaskPresetContract } from "./task-preset-contract-drift.mjs";
|
|
9
|
+
import { parseTaskBudget } from "./task-metadata.mjs";
|
|
10
|
+
import { createScannerTaskRepository, parsePhases, readVisualMapContractFile, taskPlanPathFromRecord } from "./task-repository.mjs";
|
|
12
11
|
import { normalizeReviewBoolean, reviewFindingColumns } from "./task-review-model.mjs";
|
|
13
12
|
import { allowedPhaseActors, allowedPhaseKinds } from "./phase-kind.mjs";
|
|
14
13
|
import { validateTaskCompletionConsistency } from "./task-completion-consistency.mjs";
|
|
@@ -19,6 +18,9 @@ import { summarizeGitState } from "./git-status-summary.mjs";
|
|
|
19
18
|
import { buildStatusData } from "./status-builder.mjs";
|
|
20
19
|
import { legacyCloseoutFile, legacyCompatMode, legacyLedgerFile, legacyPath, legacyPlanningRoot, legacyWalkthroughRoot, safeAdoptionCapability, } from "./harness-paths.mjs";
|
|
21
20
|
export { renderDashboard } from "./status-dashboard-renderer.mjs";
|
|
21
|
+
function errorMessage(error) {
|
|
22
|
+
return error instanceof Error ? error.message : String(error);
|
|
23
|
+
}
|
|
22
24
|
export function runCompatibilityCheck(target) {
|
|
23
25
|
const checkTarget = target.docsOnly ? target.projectRoot : target.input;
|
|
24
26
|
const result = spawnSync(process.execPath, [bundledCheckScript, checkTarget], {
|
|
@@ -125,10 +127,12 @@ export function validateReviewSchema(target, { strict = true } = {}) {
|
|
|
125
127
|
}
|
|
126
128
|
return { failures, warnings };
|
|
127
129
|
}
|
|
128
|
-
export function validateVisualMaps(target, {
|
|
130
|
+
export function validateVisualMaps(target, { tasks } = {}) {
|
|
129
131
|
const failures = [];
|
|
130
132
|
const warnings = [];
|
|
131
|
-
|
|
133
|
+
const taskRecords = tasks || createScannerTaskRepository(target).list();
|
|
134
|
+
for (const task of taskRecords) {
|
|
135
|
+
const taskPlanPath = taskPlanPathFromRecord(target, task);
|
|
132
136
|
const taskDir = path.dirname(taskPlanPath);
|
|
133
137
|
const visualMapPath = path.join(taskDir, visualMapFile);
|
|
134
138
|
const legacyPath = path.join(taskDir, legacyVisualRoadmapFile);
|
|
@@ -180,14 +184,14 @@ export function validateVisualMaps(target, { taskPlanPaths } = {}) {
|
|
|
180
184
|
return { failures, warnings };
|
|
181
185
|
}
|
|
182
186
|
export function validateTaskPresetContracts(target, { tasks } = {}) {
|
|
183
|
-
const failures = [];
|
|
187
|
+
const failures = [], warnings = [];
|
|
184
188
|
const allowedMigrationLevels = new Set([
|
|
185
189
|
"migration-baseline",
|
|
186
190
|
"migration-current-cutover",
|
|
187
191
|
"migration-full-cutover",
|
|
188
192
|
"migration-deferred",
|
|
189
193
|
]);
|
|
190
|
-
for (const task of tasks ||
|
|
194
|
+
for (const task of tasks || createScannerTaskRepository(target).list()) {
|
|
191
195
|
if (!task.taskPreset || task.taskPreset === "none")
|
|
192
196
|
continue;
|
|
193
197
|
let presetPackage = null;
|
|
@@ -195,26 +199,14 @@ export function validateTaskPresetContracts(target, { tasks } = {}) {
|
|
|
195
199
|
presetPackage = readPresetPackage(task.taskPreset, { targetInput: target.projectRoot });
|
|
196
200
|
}
|
|
197
201
|
catch (error) {
|
|
198
|
-
failures.push(`${task.path} unsupported Task Preset: ${task.taskPreset} (${error
|
|
202
|
+
failures.push(`${task.path} unsupported Task Preset: ${task.taskPreset} (${errorMessage(error)})`);
|
|
199
203
|
continue;
|
|
200
204
|
}
|
|
201
|
-
if (presetPackage?.task?.kind && task.taskKind !== presetPackage.task.kind) {
|
|
202
|
-
failures.push(`${task.path} ${task.taskPreset} preset Task Kind mismatch: expected ${presetPackage.task.kind}, got ${task.taskKind || "(missing)"}`);
|
|
203
|
-
}
|
|
204
|
-
if (String(task.presetVersion || "") !== String(presetPackage.version)) {
|
|
205
|
-
failures.push(`${task.path} ${task.taskPreset} preset missing Preset Version ${presetPackage.version}`);
|
|
206
|
-
}
|
|
207
|
-
if (task.taskPreset !== "lesson-sedimentation" && (presetPackage.evidence?.bundleDir || presetPackage.audit?.evidenceFiles?.length || Object.keys(presetPackage.evidence?.files || {}).length)) {
|
|
208
|
-
if (!task.evidenceBundle)
|
|
209
|
-
failures.push(`${task.path} ${task.taskPreset} preset missing Evidence Bundle`);
|
|
210
|
-
else if (!fs.existsSync(path.join(target.projectRoot, String(task.evidenceBundle).replace(/^TARGET:/, "").replace(/^\/+/, "")))) {
|
|
211
|
-
failures.push(`${task.path} ${task.taskPreset} preset Evidence Bundle missing: ${task.evidenceBundle}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
205
|
if (task.taskPreset !== "lesson-sedimentation") {
|
|
215
|
-
|
|
206
|
+
const regularPreset = validateRegularTaskPresetContract(target, task, presetPackage);
|
|
207
|
+
failures.push(...regularPreset.failures);
|
|
208
|
+
warnings.push(...regularPreset.warnings);
|
|
216
209
|
}
|
|
217
|
-
failures.push(...validatePresetResourcesForTask(target, task, presetPackage));
|
|
218
210
|
if (task.taskPreset === "lesson-sedimentation") {
|
|
219
211
|
if (!["standard", "complex"].includes(task.budget))
|
|
220
212
|
failures.push(`${task.path} lesson-sedimentation preset requires Selected budget: standard or complex`);
|
|
@@ -222,9 +214,8 @@ export function validateTaskPresetContracts(target, { tasks } = {}) {
|
|
|
222
214
|
failures.push(`${task.path} lesson-sedimentation preset missing task plan`);
|
|
223
215
|
continue;
|
|
224
216
|
}
|
|
225
|
-
if (task.taskPreset !== "legacy-migration")
|
|
217
|
+
if (task.taskPreset !== "legacy-migration")
|
|
226
218
|
continue;
|
|
227
|
-
}
|
|
228
219
|
if (task.budget !== "complex")
|
|
229
220
|
failures.push(`${task.path} legacy-migration preset requires Selected budget: complex`);
|
|
230
221
|
if (!allowedMigrationLevels.has(task.migrationTargetLevel)) {
|
|
@@ -241,34 +232,34 @@ export function validateTaskPresetContracts(target, { tasks } = {}) {
|
|
|
241
232
|
failures.push(`${task.path} legacy-migration preset Evidence Bundle missing session.json`);
|
|
242
233
|
}
|
|
243
234
|
if (achievedLevel === "migration-full-cutover") {
|
|
244
|
-
const snapshot = task.migrationSnapshot
|
|
235
|
+
const snapshot = task.migrationSnapshot;
|
|
245
236
|
const blockers = [];
|
|
246
|
-
if (!snapshot
|
|
237
|
+
if (!snapshot?.sessionPresent)
|
|
247
238
|
blockers.push("missing session evidence");
|
|
248
|
-
if (snapshot
|
|
249
|
-
blockers.push(`session result is ${snapshot
|
|
250
|
-
if (snapshot
|
|
239
|
+
if (snapshot?.sessionResult !== "complete")
|
|
240
|
+
blockers.push(`session result is ${snapshot?.sessionResult || "(missing)"}`);
|
|
241
|
+
if (snapshot?.strictDeferred)
|
|
251
242
|
blockers.push("strictDeferred is present");
|
|
252
|
-
if (snapshot
|
|
253
|
-
blockers.push(`strict status is ${snapshot
|
|
243
|
+
if (snapshot?.strictStatus !== "pass")
|
|
244
|
+
blockers.push(`strict status is ${snapshot?.strictStatus || "(missing)"}`);
|
|
254
245
|
for (const [field, value] of [
|
|
255
|
-
["warnings", snapshot
|
|
256
|
-
["taskActions", snapshot
|
|
257
|
-
["reviewSchemaGaps", snapshot
|
|
258
|
-
["legacyReferenceGaps", snapshot
|
|
259
|
-
["legacyResiduals", snapshot
|
|
246
|
+
["warnings", snapshot?.warnings],
|
|
247
|
+
["taskActions", snapshot?.taskActions],
|
|
248
|
+
["reviewSchemaGaps", snapshot?.reviewSchemaGaps],
|
|
249
|
+
["legacyReferenceGaps", snapshot?.legacyReferenceGaps],
|
|
250
|
+
["legacyResiduals", snapshot?.legacyResiduals],
|
|
260
251
|
]) {
|
|
261
252
|
if (Number(value || 0) !== 0)
|
|
262
253
|
blockers.push(`${field}=${value}`);
|
|
263
254
|
}
|
|
264
|
-
if (snapshot
|
|
255
|
+
if (snapshot?.fullCutoverEligible !== true)
|
|
265
256
|
blockers.push("fullCutoverEligible is not true");
|
|
266
257
|
if (blockers.length) {
|
|
267
258
|
failures.push(`${task.path} migration-full-cutover is not proven: ${blockers.join("; ")}`);
|
|
268
259
|
}
|
|
269
260
|
}
|
|
270
261
|
}
|
|
271
|
-
return { failures, warnings
|
|
262
|
+
return { failures, warnings };
|
|
272
263
|
}
|
|
273
264
|
export function validateContextDocs(target, { strict = true } = {}) {
|
|
274
265
|
const failures = [];
|
|
@@ -351,12 +342,11 @@ export function buildStatus(targetInput, options = {}) {
|
|
|
351
342
|
const shouldRunLegacy = target.harness?.version !== 2 && !options.skipLegacyCheck && (capabilityState.registry.mode === legacyCompatMode || safeAdoptionMode);
|
|
352
343
|
const legacy = shouldRunLegacy ? runCompatibilityCheck(target) : { status: "skipped", code: 0, stdout: "", stderr: "" };
|
|
353
344
|
const contractStrict = Boolean(options.strict) || (capabilityState.registry.mode !== legacyCompatMode && !safeAdoptionMode);
|
|
354
|
-
const taskPlanPaths = listTaskPlanPaths(target);
|
|
355
345
|
const closeoutContent = target.harness?.version === 2 ? "" : readFileSafe(path.join(target.projectRoot, legacyPath(legacyCloseoutFile)));
|
|
356
|
-
const tasks =
|
|
346
|
+
const tasks = createScannerTaskRepository(target, { requireGeneratedScaffoldProvenance: contractStrict, closeoutContent }).list();
|
|
357
347
|
const reviews = validateReviewSchema(target, { strict: contractStrict });
|
|
358
|
-
const visualMaps = validateVisualMaps(target, {
|
|
359
|
-
const planContracts = validatePlanContracts(target, { strict: contractStrict,
|
|
348
|
+
const visualMaps = validateVisualMaps(target, { tasks });
|
|
349
|
+
const planContracts = validatePlanContracts(target, { strict: contractStrict, tasks });
|
|
360
350
|
const presetContracts = validateTaskPresetContracts(target, { tasks });
|
|
361
351
|
const contextDocs = validateContextDocs(target, { strict: contractStrict });
|
|
362
352
|
const governanceBoundaries = validateGovernanceTableBoundaries(target);
|
|
@@ -379,10 +369,13 @@ export function buildStatus(targetInput, options = {}) {
|
|
|
379
369
|
const briefMissing = tasks.length - briefReady;
|
|
380
370
|
for (const task of tasks) {
|
|
381
371
|
for (const issue of task.materialIssues || []) {
|
|
382
|
-
|
|
372
|
+
const forceFailure = Boolean(issue.enforceFailure);
|
|
373
|
+
if (!String(issue.code || "").startsWith("missing-task-audit") &&
|
|
374
|
+
!String(issue.code || "").startsWith("legacy-") &&
|
|
375
|
+
issue.code !== "unedited-template-material")
|
|
383
376
|
continue;
|
|
384
377
|
const message = `${String(issue.sourcePath || task.path).replace(/^TARGET:/, "")} ${issue.message}`;
|
|
385
|
-
if (contractStrict || options.strictLegacy)
|
|
378
|
+
if (forceFailure || contractStrict || options.strictLegacy)
|
|
386
379
|
failures.push(message);
|
|
387
380
|
else
|
|
388
381
|
warnings.push(`adoption-needed: ${message}`);
|