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
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
const surfaceOrder = [
|
|
3
|
+
"ts-runtime",
|
|
4
|
+
"type-island",
|
|
5
|
+
"runtime-build",
|
|
6
|
+
"built-test-runner",
|
|
7
|
+
"dashboard-data",
|
|
8
|
+
"template-schema",
|
|
9
|
+
"dashboard-ui",
|
|
10
|
+
"docs-release",
|
|
11
|
+
"preset-package",
|
|
12
|
+
"package-boundary",
|
|
13
|
+
"migration-legacy",
|
|
14
|
+
"multi-repo-contract",
|
|
15
|
+
"ci-pr-evidence",
|
|
16
|
+
"root-contributor-docs",
|
|
17
|
+
"skill-reference-distribution",
|
|
18
|
+
"fixture-golden",
|
|
19
|
+
"gui-submodule",
|
|
20
|
+
"private-local-only",
|
|
21
|
+
"unknown",
|
|
22
|
+
];
|
|
23
|
+
const runtimeLayerOrder = ["ts-source", "runtime-emit", "built-test", "package-runtime", "installed-target", "not-runtime"];
|
|
24
|
+
const gateOrder = [
|
|
25
|
+
"typecheck",
|
|
26
|
+
"typecheck-guards",
|
|
27
|
+
"import-graph",
|
|
28
|
+
"dist-drift",
|
|
29
|
+
"runtime-emit-contract",
|
|
30
|
+
"dist-build-pipeline",
|
|
31
|
+
"dist-observation",
|
|
32
|
+
"temp-project-cli-smoke",
|
|
33
|
+
"status-json-contract",
|
|
34
|
+
"built-tests",
|
|
35
|
+
"target-project-check",
|
|
36
|
+
"dashboard-smoke",
|
|
37
|
+
"source-package-check",
|
|
38
|
+
"pack-dry-run",
|
|
39
|
+
"docs-leak-link-check",
|
|
40
|
+
"compatibility-policy",
|
|
41
|
+
"maintainer-review",
|
|
42
|
+
"dashboard-data-golden",
|
|
43
|
+
"preset-contract",
|
|
44
|
+
"migration-fixture",
|
|
45
|
+
"workflow-lint",
|
|
46
|
+
"fixture-manifest",
|
|
47
|
+
"gui-check",
|
|
48
|
+
"command-consistency",
|
|
49
|
+
];
|
|
50
|
+
const compatibilityPolicies = ["legacy-support", "migration-required", "hard-cutover"];
|
|
51
|
+
const surfaceGateMap = {
|
|
52
|
+
"ts-runtime": ["typecheck", "typecheck-guards", "import-graph", "dist-drift"],
|
|
53
|
+
"type-island": ["typecheck", "typecheck-guards", "import-graph"],
|
|
54
|
+
"runtime-build": ["runtime-emit-contract", "dist-build-pipeline", "dist-observation", "dist-drift"],
|
|
55
|
+
"built-test-runner": ["built-tests", "typecheck"],
|
|
56
|
+
"dashboard-data": ["dashboard-data-golden", "status-json-contract", "dashboard-smoke"],
|
|
57
|
+
"template-schema": ["target-project-check", "compatibility-policy"],
|
|
58
|
+
"dashboard-ui": ["dashboard-smoke"],
|
|
59
|
+
"docs-release": ["docs-leak-link-check", "source-package-check"],
|
|
60
|
+
"preset-package": ["preset-contract", "source-package-check", "compatibility-policy"],
|
|
61
|
+
"package-boundary": ["source-package-check", "pack-dry-run", "compatibility-policy"],
|
|
62
|
+
"migration-legacy": ["migration-fixture", "compatibility-policy"],
|
|
63
|
+
"multi-repo-contract": ["target-project-check", "maintainer-review"],
|
|
64
|
+
"ci-pr-evidence": ["workflow-lint", "maintainer-review"],
|
|
65
|
+
"root-contributor-docs": ["docs-leak-link-check", "source-package-check", "command-consistency"],
|
|
66
|
+
"skill-reference-distribution": ["source-package-check", "pack-dry-run"],
|
|
67
|
+
"fixture-golden": ["fixture-manifest", "target-project-check"],
|
|
68
|
+
"gui-submodule": ["gui-check"],
|
|
69
|
+
"private-local-only": ["source-package-check", "maintainer-review"],
|
|
70
|
+
"unknown": ["maintainer-review"],
|
|
71
|
+
};
|
|
72
|
+
const gateReasonMap = {
|
|
73
|
+
typecheck: "TypeScript source changes must satisfy the enforced strict type gate.",
|
|
74
|
+
"typecheck-guards": "Type boundary and no-ts-nocheck guards protect shared type islands and implementation coverage.",
|
|
75
|
+
"import-graph": "Runtime and type island changes can introduce unresolved imports, cycles, or invalid type imports.",
|
|
76
|
+
"dist-drift": "Runtime source or emit changes can make committed/package dist drift from TypeScript source.",
|
|
77
|
+
"runtime-emit-contract": "Runtime build surfaces must prove TypeScript emits package-safe JavaScript.",
|
|
78
|
+
"dist-build-pipeline": "Runtime build changes must prove the dist build pipeline still emits required package files.",
|
|
79
|
+
"dist-observation": "Runtime/package changes need dist observation evidence across source, package, and command surfaces.",
|
|
80
|
+
"temp-project-cli-smoke": "CLI/runtime changes need target-project smoke evidence for the installed command path.",
|
|
81
|
+
"status-json-contract": "Status, scanner, and CLI JSON surfaces need machine-readable contract evidence.",
|
|
82
|
+
"built-tests": "Built tests prove the compiled test runner still exercises the package runtime shape.",
|
|
83
|
+
"target-project-check": "Target project fixtures prove templates, examples, and contracts remain usable.",
|
|
84
|
+
"dashboard-smoke": "Dashboard surfaces need smoke coverage for data and UI consumption.",
|
|
85
|
+
"source-package-check": "Package-facing changes must not publish private or local-only source state.",
|
|
86
|
+
"pack-dry-run": "Package boundary changes need file-list and npm packaging evidence.",
|
|
87
|
+
"docs-leak-link-check": "Public documentation changes need leak and link/path validation.",
|
|
88
|
+
"compatibility-policy": "Contract-changing surfaces must declare legacy-support, migration-required, or hard-cutover.",
|
|
89
|
+
"maintainer-review": "Unknown or high-cost surfaces require maintainer review before contributor verify can pass.",
|
|
90
|
+
"dashboard-data-golden": "Dashboard data contract changes need golden or schema evidence.",
|
|
91
|
+
"preset-contract": "Preset changes need install, resource, and safety contract evidence.",
|
|
92
|
+
"migration-fixture": "Migration and legacy changes need fixture evidence for supported or rejected formats.",
|
|
93
|
+
"workflow-lint": "CI and PR evidence changes need workflow and path-filter validation.",
|
|
94
|
+
"fixture-manifest": "Fixture and golden changes need drift policy evidence.",
|
|
95
|
+
"gui-check": "GUI submodule changes need path-filtered GUI validation or pointer evidence.",
|
|
96
|
+
"command-consistency": "Contributor docs must stay consistent with actual package commands and profiles.",
|
|
97
|
+
};
|
|
98
|
+
export function classifyImpact(changedFiles) {
|
|
99
|
+
const classifiedFiles = changedFiles.map(classifyChangedFile);
|
|
100
|
+
const surfaces = orderedUnique(classifiedFiles.flatMap((file) => file.surfaces), surfaceOrder);
|
|
101
|
+
const runtimeLayers = orderedUnique(classifiedFiles.flatMap((file) => file.runtimeLayers), runtimeLayerOrder);
|
|
102
|
+
const requiredGateIds = orderedUnique(classifiedFiles.flatMap((file) => file.requiredGates), gateOrder);
|
|
103
|
+
const compatibilityRequirements = classifiedFiles.flatMap((file) => buildCompatibilityRequirements(file));
|
|
104
|
+
return {
|
|
105
|
+
changedFiles: classifiedFiles,
|
|
106
|
+
surfaces,
|
|
107
|
+
runtimeLayers,
|
|
108
|
+
requiredGates: requiredGateIds.map((gate) => ({
|
|
109
|
+
id: gate,
|
|
110
|
+
blocking: gate === "maintainer-review" ? "maintainer-review" : "required",
|
|
111
|
+
reason: gateReasonMap[gate],
|
|
112
|
+
surfaces: surfacesForGate(surfaces, gate),
|
|
113
|
+
})),
|
|
114
|
+
unknownSurfaces: classifiedFiles.flatMap((file) => buildUnknownSurface(file)),
|
|
115
|
+
compatibilityPolicyRequired: compatibilityRequirements.length > 0,
|
|
116
|
+
compatibilityPolicyRequirements: compatibilityRequirements,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function classifyChangedFile(record) {
|
|
120
|
+
const normalizedPath = normalizeChangedPath(record.path);
|
|
121
|
+
const normalizedOldPath = record.oldPath ? normalizeChangedPath(record.oldPath) : undefined;
|
|
122
|
+
const pathClassifications = [
|
|
123
|
+
classifyPath(normalizedPath, record),
|
|
124
|
+
...(normalizedOldPath && normalizedOldPath !== normalizedPath ? [classifyPath(normalizedOldPath, record)] : []),
|
|
125
|
+
];
|
|
126
|
+
const surfaces = orderedUnique(pathClassifications.flatMap((classification) => classification.surfaces), surfaceOrder);
|
|
127
|
+
const runtimeLayers = orderedUnique(pathClassifications.flatMap((classification) => classification.runtimeLayers), runtimeLayerOrder);
|
|
128
|
+
const requiredGates = orderedUnique([...surfaces.flatMap((surface) => surfaceGateMap[surface]), ...pathClassifications.flatMap((classification) => classification.requiredGates)], gateOrder);
|
|
129
|
+
return {
|
|
130
|
+
path: normalizedPath,
|
|
131
|
+
status: record.status,
|
|
132
|
+
...(normalizedOldPath ? { oldPath: normalizedOldPath } : {}),
|
|
133
|
+
surfaces,
|
|
134
|
+
runtimeLayers,
|
|
135
|
+
requiredGates,
|
|
136
|
+
compatibilityPolicyRequired: pathClassifications.some((classification) => classification.compatibilityPolicyRequired),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function classifyPath(relativePath, record) {
|
|
140
|
+
const surfaces = [];
|
|
141
|
+
const runtimeLayers = [];
|
|
142
|
+
const requiredGates = [];
|
|
143
|
+
let compatibilityPolicyRequired = false;
|
|
144
|
+
const addSurface = (surface) => {
|
|
145
|
+
surfaces.push(surface);
|
|
146
|
+
};
|
|
147
|
+
const addLayer = (layer) => {
|
|
148
|
+
runtimeLayers.push(layer);
|
|
149
|
+
};
|
|
150
|
+
const requirePolicy = () => {
|
|
151
|
+
compatibilityPolicyRequired = true;
|
|
152
|
+
};
|
|
153
|
+
const requireGate = (gate) => {
|
|
154
|
+
requiredGates.push(gate);
|
|
155
|
+
};
|
|
156
|
+
if (!isRepositoryRelativePath(relativePath)) {
|
|
157
|
+
addSurface("unknown");
|
|
158
|
+
addLayer("not-runtime");
|
|
159
|
+
return { surfaces: orderedUnique(surfaces, surfaceOrder), runtimeLayers: orderedUnique(runtimeLayers, runtimeLayerOrder), compatibilityPolicyRequired, requiredGates: orderedUnique(requiredGates, gateOrder) };
|
|
160
|
+
}
|
|
161
|
+
if (isPrivateLocalOnlyPath(relativePath)) {
|
|
162
|
+
addSurface("private-local-only");
|
|
163
|
+
addLayer("not-runtime");
|
|
164
|
+
return { surfaces: orderedUnique(surfaces, surfaceOrder), runtimeLayers: orderedUnique(runtimeLayers, runtimeLayerOrder), compatibilityPolicyRequired, requiredGates: orderedUnique(requiredGates, gateOrder) };
|
|
165
|
+
}
|
|
166
|
+
if (record.isSubmodule || relativePath === "harness-gui" || relativePath.startsWith("harness-gui/")) {
|
|
167
|
+
addSurface("gui-submodule");
|
|
168
|
+
addLayer("not-runtime");
|
|
169
|
+
}
|
|
170
|
+
if (relativePath.startsWith(".github/")) {
|
|
171
|
+
addSurface("ci-pr-evidence");
|
|
172
|
+
addLayer("not-runtime");
|
|
173
|
+
if (relativePath === ".github/pull_request_template.md")
|
|
174
|
+
requirePolicy();
|
|
175
|
+
}
|
|
176
|
+
if (isTypeIslandPath(relativePath)) {
|
|
177
|
+
addSurface("type-island");
|
|
178
|
+
addLayer("ts-source");
|
|
179
|
+
}
|
|
180
|
+
if (isCliContractPath(relativePath)) {
|
|
181
|
+
requireGate("temp-project-cli-smoke");
|
|
182
|
+
requireGate("status-json-contract");
|
|
183
|
+
requirePolicy();
|
|
184
|
+
}
|
|
185
|
+
if (isTsRuntimePath(relativePath)) {
|
|
186
|
+
addSurface("ts-runtime");
|
|
187
|
+
addLayer("ts-source");
|
|
188
|
+
}
|
|
189
|
+
if (isRuntimeBuildPath(relativePath)) {
|
|
190
|
+
addSurface("runtime-build");
|
|
191
|
+
addLayer("runtime-emit");
|
|
192
|
+
if (relativePath === "tsconfig.json")
|
|
193
|
+
addLayer("ts-source");
|
|
194
|
+
if (relativePath.startsWith("dist/")) {
|
|
195
|
+
addSurface("package-boundary");
|
|
196
|
+
addLayer("package-runtime");
|
|
197
|
+
requirePolicy();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (isBuiltTestRunnerPath(relativePath)) {
|
|
201
|
+
addSurface("built-test-runner");
|
|
202
|
+
addLayer("built-test");
|
|
203
|
+
if (/^tests\/.*\.mts$/.test(relativePath))
|
|
204
|
+
addLayer("ts-source");
|
|
205
|
+
}
|
|
206
|
+
if (isDashboardDataPath(relativePath)) {
|
|
207
|
+
addSurface("dashboard-data");
|
|
208
|
+
requirePolicy();
|
|
209
|
+
if (relativePath.startsWith("scripts/"))
|
|
210
|
+
addLayer("ts-source");
|
|
211
|
+
}
|
|
212
|
+
if (relativePath.startsWith("templates/dashboard/assets/")) {
|
|
213
|
+
addSurface("dashboard-ui");
|
|
214
|
+
addLayer("installed-target");
|
|
215
|
+
if (relativePath.includes("manifest"))
|
|
216
|
+
requirePolicy();
|
|
217
|
+
}
|
|
218
|
+
else if (relativePath.startsWith("templates/") || relativePath.startsWith("templates-zh-CN/")) {
|
|
219
|
+
addSurface("template-schema");
|
|
220
|
+
addLayer("installed-target");
|
|
221
|
+
requirePolicy();
|
|
222
|
+
}
|
|
223
|
+
if (relativePath.startsWith("docs-release/")) {
|
|
224
|
+
addSurface("docs-release");
|
|
225
|
+
addLayer("not-runtime");
|
|
226
|
+
if (relativePath.includes("parent-control-repository-pattern"))
|
|
227
|
+
addSurface("multi-repo-contract");
|
|
228
|
+
}
|
|
229
|
+
if (isRootContributorDoc(relativePath)) {
|
|
230
|
+
addSurface("root-contributor-docs");
|
|
231
|
+
addLayer("not-runtime");
|
|
232
|
+
}
|
|
233
|
+
if (isPresetPackagePath(relativePath)) {
|
|
234
|
+
addSurface("preset-package");
|
|
235
|
+
addLayer(relativePath.startsWith("scripts/") ? "ts-source" : "installed-target");
|
|
236
|
+
requirePolicy();
|
|
237
|
+
}
|
|
238
|
+
if (isPackageBoundaryPath(relativePath)) {
|
|
239
|
+
addSurface("package-boundary");
|
|
240
|
+
addLayer("package-runtime");
|
|
241
|
+
requirePolicy();
|
|
242
|
+
}
|
|
243
|
+
if (isMigrationPath(relativePath)) {
|
|
244
|
+
addSurface("migration-legacy");
|
|
245
|
+
if (relativePath.startsWith("scripts/"))
|
|
246
|
+
addLayer("ts-source");
|
|
247
|
+
requirePolicy();
|
|
248
|
+
}
|
|
249
|
+
if (isSkillReferenceDistributionPath(relativePath)) {
|
|
250
|
+
addSurface("skill-reference-distribution");
|
|
251
|
+
addLayer("installed-target");
|
|
252
|
+
}
|
|
253
|
+
if (isFixtureGoldenPath(relativePath)) {
|
|
254
|
+
addSurface("fixture-golden");
|
|
255
|
+
addLayer(relativePath.startsWith("examples/") ? "installed-target" : "not-runtime");
|
|
256
|
+
}
|
|
257
|
+
if (surfaces.length === 0) {
|
|
258
|
+
addSurface("unknown");
|
|
259
|
+
addLayer("not-runtime");
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
surfaces: orderedUnique(surfaces, surfaceOrder),
|
|
263
|
+
runtimeLayers: orderedUnique(runtimeLayers, runtimeLayerOrder),
|
|
264
|
+
compatibilityPolicyRequired,
|
|
265
|
+
requiredGates: orderedUnique(requiredGates, gateOrder),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function buildCompatibilityRequirements(file) {
|
|
269
|
+
if (!file.compatibilityPolicyRequired)
|
|
270
|
+
return [];
|
|
271
|
+
const policySurfaces = file.surfaces.filter((surface) => surfaceRequiresCompatibilityPolicy(surface));
|
|
272
|
+
const surfaces = policySurfaces.length > 0 ? policySurfaces : file.surfaces;
|
|
273
|
+
return surfaces.map((surface) => ({
|
|
274
|
+
path: file.path,
|
|
275
|
+
surface,
|
|
276
|
+
allowedPolicies: compatibilityPolicies,
|
|
277
|
+
reason: compatibilityReason(surface),
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
function buildUnknownSurface(file) {
|
|
281
|
+
if (!file.surfaces.includes("unknown"))
|
|
282
|
+
return [];
|
|
283
|
+
return [
|
|
284
|
+
{
|
|
285
|
+
path: file.path,
|
|
286
|
+
status: file.status,
|
|
287
|
+
reason: "No public impact surface matched this repository-relative path.",
|
|
288
|
+
},
|
|
289
|
+
];
|
|
290
|
+
}
|
|
291
|
+
function surfacesForGate(surfaces, gate) {
|
|
292
|
+
if ((gate === "temp-project-cli-smoke" || gate === "status-json-contract") && surfaces.includes("ts-runtime")) {
|
|
293
|
+
return ["ts-runtime", ...surfaces.filter((surface) => surface !== "ts-runtime" && surfaceGateMap[surface].includes(gate))];
|
|
294
|
+
}
|
|
295
|
+
return surfaces.filter((surface) => surfaceGateMap[surface].includes(gate));
|
|
296
|
+
}
|
|
297
|
+
function surfaceRequiresCompatibilityPolicy(surface) {
|
|
298
|
+
return [
|
|
299
|
+
"ts-runtime",
|
|
300
|
+
"dashboard-data",
|
|
301
|
+
"template-schema",
|
|
302
|
+
"preset-package",
|
|
303
|
+
"package-boundary",
|
|
304
|
+
"migration-legacy",
|
|
305
|
+
"ci-pr-evidence",
|
|
306
|
+
].includes(surface);
|
|
307
|
+
}
|
|
308
|
+
function compatibilityReason(surface) {
|
|
309
|
+
switch (surface) {
|
|
310
|
+
case "ts-runtime":
|
|
311
|
+
return "CLI output, exit code, or machine-readable report contract may change.";
|
|
312
|
+
case "dashboard-data":
|
|
313
|
+
return "Dashboard data JSON or public manifest contract may change.";
|
|
314
|
+
case "template-schema":
|
|
315
|
+
return "Task schema, template frontmatter, or target-project layout may change.";
|
|
316
|
+
case "preset-package":
|
|
317
|
+
return "Preset registry, installed resources, or action contract may change.";
|
|
318
|
+
case "package-boundary":
|
|
319
|
+
return "Package file list or installed runtime layout may change.";
|
|
320
|
+
case "migration-legacy":
|
|
321
|
+
return "Migration or legacy adoption behavior may change.";
|
|
322
|
+
case "ci-pr-evidence":
|
|
323
|
+
return "PR evidence or workflow contract may change.";
|
|
324
|
+
default:
|
|
325
|
+
return "Contract-changing surface requires explicit compatibility policy.";
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function normalizeChangedPath(inputPath) {
|
|
329
|
+
const normalized = inputPath.trim().replaceAll("\\", "/").replace(/\/+/g, "/");
|
|
330
|
+
if (normalized === ".")
|
|
331
|
+
return "";
|
|
332
|
+
return normalized.startsWith("./") ? normalized.slice(2) : normalized;
|
|
333
|
+
}
|
|
334
|
+
function isRepositoryRelativePath(relativePath) {
|
|
335
|
+
return relativePath.length > 0 && !relativePath.startsWith("/") && !relativePath.startsWith("../") && !path.isAbsolute(relativePath);
|
|
336
|
+
}
|
|
337
|
+
function isPrivateLocalOnlyPath(relativePath) {
|
|
338
|
+
return (relativePath === "AGENTS.md" ||
|
|
339
|
+
relativePath === "CLAUDE.md" ||
|
|
340
|
+
relativePath === "docs" ||
|
|
341
|
+
relativePath.startsWith("docs/") ||
|
|
342
|
+
relativePath === ".harness-private" ||
|
|
343
|
+
relativePath.startsWith(".harness-private/"));
|
|
344
|
+
}
|
|
345
|
+
function isTypeIslandPath(relativePath) {
|
|
346
|
+
return relativePath.startsWith("scripts/lib/types/") || /^tests\/helpers\/.*types.*\.(mts|ts)$/.test(relativePath);
|
|
347
|
+
}
|
|
348
|
+
function isTsRuntimePath(relativePath) {
|
|
349
|
+
return relativePath.startsWith("scripts/") && relativePath.endsWith(".mts");
|
|
350
|
+
}
|
|
351
|
+
function isCliContractPath(relativePath) {
|
|
352
|
+
return (relativePath === "scripts/harness.mts" ||
|
|
353
|
+
relativePath.startsWith("scripts/commands/") ||
|
|
354
|
+
relativePath === "scripts/lib/types/impact.ts" ||
|
|
355
|
+
relativePath === "scripts/lib/types/task-lifecycle.ts" ||
|
|
356
|
+
relativePath === "scripts/lib/check-profiles.mts" ||
|
|
357
|
+
relativePath === "scripts/lib/status-builder.mts" ||
|
|
358
|
+
relativePath === "scripts/lib/task-index.mts" ||
|
|
359
|
+
relativePath === "scripts/lib/task-lifecycle.mts");
|
|
360
|
+
}
|
|
361
|
+
function isRuntimeBuildPath(relativePath) {
|
|
362
|
+
return (relativePath === "scripts/build-dist.mts" ||
|
|
363
|
+
relativePath === "scripts/check-runtime-emit.mts" ||
|
|
364
|
+
relativePath === "scripts/check-dist-observation.mts" ||
|
|
365
|
+
/^tsconfig(?:\.[A-Za-z-]+)?\.json$/.test(relativePath) ||
|
|
366
|
+
relativePath.startsWith("dist/"));
|
|
367
|
+
}
|
|
368
|
+
function isBuiltTestRunnerPath(relativePath) {
|
|
369
|
+
return relativePath === "scripts/run-built-tests.mts" || relativePath === "tests/run-all.mts" || relativePath === "tsconfig.tests.json" || /^tests\/.*\.mts$/.test(relativePath);
|
|
370
|
+
}
|
|
371
|
+
function isDashboardDataPath(relativePath) {
|
|
372
|
+
return (relativePath === "scripts/lib/dashboard-data.mts" ||
|
|
373
|
+
relativePath === "scripts/lib/check-profiles.mts" ||
|
|
374
|
+
relativePath === "scripts/lib/status-builder.mts" ||
|
|
375
|
+
relativePath === "scripts/lib/status-dashboard-renderer.mts" ||
|
|
376
|
+
relativePath === "scripts/lib/task-scanner.mts" ||
|
|
377
|
+
relativePath === "scripts/lib/task-review-model.mts" ||
|
|
378
|
+
relativePath === "scripts/lib/task-index.mts" ||
|
|
379
|
+
relativePath === "scripts/lib/types/check-profiles.ts" ||
|
|
380
|
+
relativePath === "scripts/lib/types/task-scanner.ts" ||
|
|
381
|
+
relativePath === "scripts/lib/types/impact.ts" ||
|
|
382
|
+
relativePath.startsWith("tests/golden/dashboard/"));
|
|
383
|
+
}
|
|
384
|
+
function isRootContributorDoc(relativePath) {
|
|
385
|
+
return /^README(\.[A-Za-z-]+)?\.md$/.test(relativePath) || relativePath === "CONTRIBUTING.md" || relativePath === "CHANGELOG.md";
|
|
386
|
+
}
|
|
387
|
+
function isPackageBoundaryPath(relativePath) {
|
|
388
|
+
return (relativePath === "package.json" ||
|
|
389
|
+
relativePath === "package-lock.json" ||
|
|
390
|
+
relativePath === "postinstall.mjs" ||
|
|
391
|
+
relativePath === "run-dist.mjs" ||
|
|
392
|
+
relativePath === "LICENSE" ||
|
|
393
|
+
relativePath === "LICENSE-EXCEPTION.md");
|
|
394
|
+
}
|
|
395
|
+
function isPresetPackagePath(relativePath) {
|
|
396
|
+
return (relativePath.startsWith("presets/") ||
|
|
397
|
+
relativePath === "scripts/commands/preset-command.mts" ||
|
|
398
|
+
relativePath === "scripts/lib/core-shared.mts" ||
|
|
399
|
+
relativePath === "scripts/lib/types/preset.ts" ||
|
|
400
|
+
/^scripts\/lib\/preset-.*\.mts$/.test(relativePath));
|
|
401
|
+
}
|
|
402
|
+
function isMigrationPath(relativePath) {
|
|
403
|
+
return relativePath.includes("migration") || relativePath.includes("legacy") || relativePath.includes("hard-cutover");
|
|
404
|
+
}
|
|
405
|
+
function isSkillReferenceDistributionPath(relativePath) {
|
|
406
|
+
return relativePath === "SKILL.md" || relativePath.startsWith("skills/") || relativePath.startsWith("references/");
|
|
407
|
+
}
|
|
408
|
+
function isFixtureGoldenPath(relativePath) {
|
|
409
|
+
return (relativePath.startsWith("examples/") ||
|
|
410
|
+
relativePath.startsWith("fixtures/") ||
|
|
411
|
+
relativePath.startsWith("tests/fixtures/") ||
|
|
412
|
+
relativePath.startsWith("tests/golden/"));
|
|
413
|
+
}
|
|
414
|
+
function orderedUnique(values, order) {
|
|
415
|
+
const present = new Set(values);
|
|
416
|
+
const ordered = order.filter((value) => present.has(value));
|
|
417
|
+
const orderSet = new Set(order);
|
|
418
|
+
const extra = values.filter((value, index) => !orderSet.has(value) && values.indexOf(value) === index);
|
|
419
|
+
return [...ordered, ...extra];
|
|
420
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { datePrefix, lessonCandidatesFile, normalizeTarget, readFileSafe, slug, toPosix, } from "./core-shared.mjs";
|
|
@@ -64,7 +63,7 @@ export function promoteLessonCandidate(targetInput, taskId, candidateId, { dryRu
|
|
|
64
63
|
try {
|
|
65
64
|
fs.mkdirSync(path.dirname(detailPath), { recursive: true });
|
|
66
65
|
if (!fs.existsSync(detailPath))
|
|
67
|
-
fs.writeFileSync(detailPath, renderLessonDetail({ lessonId, candidate: row, task
|
|
66
|
+
fs.writeFileSync(detailPath, renderLessonDetail({ lessonId, candidate: row, task }));
|
|
68
67
|
fs.writeFileSync(candidatePath, markCandidatePromoted(candidateContent, row.id, lessonId));
|
|
69
68
|
const commit = commitGovernanceSync(governanceContext, [
|
|
70
69
|
detailRelative,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import { sanitizeText, slug } from "./core-shared.mjs";
|
|
3
2
|
export function markdownTableRows(content) {
|
|
4
3
|
return content
|
|
@@ -89,6 +88,56 @@ export function tableAfterHeading(content, headerPattern) {
|
|
|
89
88
|
}
|
|
90
89
|
return { header, rows: body };
|
|
91
90
|
}
|
|
91
|
+
export function stripFencedCodeBlocks(content) {
|
|
92
|
+
const lines = String(content || "").split(/\r?\n/);
|
|
93
|
+
const result = [];
|
|
94
|
+
let fence = "";
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
const match = line.match(/^\s{0,3}(```|~~~)/);
|
|
97
|
+
if (match) {
|
|
98
|
+
if (!fence)
|
|
99
|
+
fence = match[1];
|
|
100
|
+
else if (match[1] === fence)
|
|
101
|
+
fence = "";
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (!fence)
|
|
105
|
+
result.push(line);
|
|
106
|
+
}
|
|
107
|
+
return result.join("\n");
|
|
108
|
+
}
|
|
109
|
+
export function removeHeadingSectionOutsideFences(content, headingPattern) {
|
|
110
|
+
const lines = String(content || "").split(/\r?\n/);
|
|
111
|
+
let fence = "";
|
|
112
|
+
let start = -1;
|
|
113
|
+
let end = lines.length;
|
|
114
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
115
|
+
const line = lines[index];
|
|
116
|
+
const fenceMatch = line.match(/^\s{0,3}(```|~~~)/);
|
|
117
|
+
if (fenceMatch) {
|
|
118
|
+
if (!fence)
|
|
119
|
+
fence = fenceMatch[1];
|
|
120
|
+
else if (fenceMatch[1] === fence)
|
|
121
|
+
fence = "";
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (fence)
|
|
125
|
+
continue;
|
|
126
|
+
if (start < 0 && headingPattern.test(line.trim())) {
|
|
127
|
+
start = index;
|
|
128
|
+
if (start > 0 && !lines[start - 1].trim())
|
|
129
|
+
start -= 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (start >= 0 && index > start && /^##\s+/.test(line.trim())) {
|
|
133
|
+
end = index;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (start < 0)
|
|
138
|
+
return String(content || "");
|
|
139
|
+
return [...lines.slice(0, start), ...lines.slice(end)].join("\n");
|
|
140
|
+
}
|
|
92
141
|
export function getColumn(header, name) {
|
|
93
142
|
return header.findIndex((cell) => cell.toLowerCase() === name.toLowerCase());
|
|
94
143
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
@@ -22,7 +21,8 @@ export function buildMigrationPlan(targetInput, { limit = 20 } = {}) {
|
|
|
22
21
|
const legacyActions = [];
|
|
23
22
|
const legacyResiduals = [];
|
|
24
23
|
const warningGroups = new Map();
|
|
25
|
-
const
|
|
24
|
+
const tasks = status.tasks;
|
|
25
|
+
const tasksByShortId = new Map(tasks.map((task) => [task.shortId, task]));
|
|
26
26
|
function addTaskAction(taskId, actionPath, fileName, actionText) {
|
|
27
27
|
const existing = taskActionsByTask.get(taskId) || {
|
|
28
28
|
taskId,
|
|
@@ -85,7 +85,7 @@ export function buildMigrationPlan(targetInput, { limit = 20 } = {}) {
|
|
|
85
85
|
const legacyVisualOnlyTasks = [];
|
|
86
86
|
const unknownClassificationTasks = [];
|
|
87
87
|
const weakBriefTasks = [];
|
|
88
|
-
for (const task of
|
|
88
|
+
for (const task of tasks) {
|
|
89
89
|
if (task.visualMapStatus === "legacy-only") {
|
|
90
90
|
legacyVisualOnlyTasks.push({
|
|
91
91
|
taskId: task.shortId,
|
|
@@ -133,7 +133,7 @@ export function buildMigrationPlan(targetInput, { limit = 20 } = {}) {
|
|
|
133
133
|
const recommendedCapabilities = recommendedMigrationCapabilities(status, target, registry);
|
|
134
134
|
const missingExecutionStrategy = taskActions.filter((action) => action.files.includes("execution_strategy.md")).length;
|
|
135
135
|
const missingVisualMap = taskActions.filter((action) => action.files.includes(visualMapFile)).length;
|
|
136
|
-
const cutoverCounters = taskCutoverCounters(
|
|
136
|
+
const cutoverCounters = taskCutoverCounters(tasks);
|
|
137
137
|
const visualMapActions = taskActions.filter((action) => action.files.includes(visualMapFile)).length;
|
|
138
138
|
const fullCutoverEligible = status.checkState.status === "pass" &&
|
|
139
139
|
taskActions.length === 0 &&
|
|
@@ -177,7 +177,7 @@ export function buildMigrationPlan(targetInput, { limit = 20 } = {}) {
|
|
|
177
177
|
fullCutoverEligible,
|
|
178
178
|
},
|
|
179
179
|
recommendedCapabilities,
|
|
180
|
-
phases: migrationPhases({ locale, recommendedCapabilities }),
|
|
180
|
+
phases: migrationPhases({ locale, recommendedCapabilities: recommendedCapabilities.map((capability) => ({ ...capability, state: "recommended" })) }),
|
|
181
181
|
taskActions: taskActions.slice(0, limit),
|
|
182
182
|
visualMapActions: taskActions.filter((action) => action.files.includes(visualMapFile)).slice(0, limit),
|
|
183
183
|
legacyVisualOnlyTasks: legacyVisualOnlyTasks.slice(0, limit),
|
|
@@ -229,6 +229,7 @@ export function runMigration(targetInput, options = {}) {
|
|
|
229
229
|
const strictStatus = buildStatus(targetInput, { strict: true, strictLegacy: true, allowLegacyTarget: true });
|
|
230
230
|
const finalPlan = buildMigrationPlan(targetInput, { limit: options.limit || 50 });
|
|
231
231
|
const afterGit = inspectGitStatus(target.projectRoot);
|
|
232
|
+
const finalTarget = normalizeTarget(targetInput);
|
|
232
233
|
const strictDeferred = strictDeferredFromStatus(strictStatus);
|
|
233
234
|
const result = options.planOnly
|
|
234
235
|
? "plan-only"
|
|
@@ -251,7 +252,7 @@ export function runMigration(targetInput, options = {}) {
|
|
|
251
252
|
source: options.locale ? "explicit" : localeProbe.mixedLanguageDetected ? "assumed-from-probe" : "probe",
|
|
252
253
|
probe: localeProbe,
|
|
253
254
|
},
|
|
254
|
-
capabilities: readCapabilityRegistry(
|
|
255
|
+
capabilities: readCapabilityRegistry(finalTarget).capabilities,
|
|
255
256
|
baseline: {
|
|
256
257
|
statusPath: path.join(sessionDir, "baseline-status.json"),
|
|
257
258
|
migratePlanPath: path.join(sessionDir, "migrate-plan.json"),
|
|
@@ -289,7 +290,7 @@ export function runMigration(targetInput, options = {}) {
|
|
|
289
290
|
return { ...session, sessionPath, reportPath };
|
|
290
291
|
}
|
|
291
292
|
export function verifyMigrationSession(sessionPathInput, { fullCutover = false } = {}) {
|
|
292
|
-
const sessionPath = path.resolve(sessionPathInput || "");
|
|
293
|
+
const sessionPath = path.resolve(String(sessionPathInput || ""));
|
|
293
294
|
if (!sessionPath || !fs.existsSync(sessionPath)) {
|
|
294
295
|
return { operation: "migrate-verify", status: "fail", failures: [`session file not found: ${sessionPathInput}`], warnings: [] };
|
|
295
296
|
}
|
|
@@ -298,7 +299,7 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
|
|
|
298
299
|
let readError = null;
|
|
299
300
|
const session = readJsonSafe(sessionPath, null, { onError: (error) => { readError = error; } });
|
|
300
301
|
if (!session)
|
|
301
|
-
return { operation: "migrate-verify", status: "fail", failures: [`invalid session json: ${readError
|
|
302
|
+
return { operation: "migrate-verify", status: "fail", failures: [`invalid session json: ${errorMessage(readError)}`], warnings };
|
|
302
303
|
if (session.operation !== "migrate-run")
|
|
303
304
|
failures.push("session operation is not migrate-run");
|
|
304
305
|
if (session.schemaVersion !== 1 && session.version !== 1)
|
|
@@ -395,19 +396,23 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
|
|
|
395
396
|
else {
|
|
396
397
|
try {
|
|
397
398
|
const dashboardBundle = JSON.parse(dataMatch[1]);
|
|
399
|
+
const dashboardStatus = asRecord(dashboardBundle.status);
|
|
400
|
+
const dashboardProject = asRecord(dashboardStatus.project);
|
|
401
|
+
const dashboardCheckState = dashboardStatus.checkState;
|
|
402
|
+
const dashboardAdoption = asRecord(dashboardBundle.adoption);
|
|
398
403
|
const expectedProjectName = session.target ? path.basename(session.target) : "";
|
|
399
|
-
if (
|
|
404
|
+
if (dashboardStatus.schemaVersion !== 2)
|
|
400
405
|
failures.push("dashboard bundle missing status schemaVersion 2");
|
|
401
|
-
if (expectedProjectName &&
|
|
402
|
-
failures.push(`dashboard bundle project ${
|
|
406
|
+
if (expectedProjectName && dashboardProject.name !== expectedProjectName) {
|
|
407
|
+
failures.push(`dashboard bundle project ${dashboardProject.name || "(none)"} does not match target ${expectedProjectName}`);
|
|
403
408
|
}
|
|
404
|
-
if (!
|
|
409
|
+
if (!dashboardCheckState)
|
|
405
410
|
failures.push("dashboard bundle missing checkState");
|
|
406
|
-
if (!Array.isArray(
|
|
411
|
+
if (!Array.isArray(dashboardAdoption.warnings))
|
|
407
412
|
failures.push("dashboard bundle missing adoption warnings array");
|
|
408
413
|
}
|
|
409
414
|
catch (error) {
|
|
410
|
-
failures.push(`dashboard-data.js contains invalid dashboard JSON: ${error
|
|
415
|
+
failures.push(`dashboard-data.js contains invalid dashboard JSON: ${errorMessage(error)}`);
|
|
411
416
|
}
|
|
412
417
|
}
|
|
413
418
|
}
|
|
@@ -422,8 +427,9 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
|
|
|
422
427
|
warnings.push(`strict cutover deferred: ${deferred.failureCount} failures`);
|
|
423
428
|
}
|
|
424
429
|
}
|
|
425
|
-
if (fullCutover)
|
|
426
|
-
validateFullCutoverSession(session, failures);
|
|
430
|
+
if (fullCutover) {
|
|
431
|
+
validateFullCutoverSession({ ...session, dashboard: session.dashboard || undefined }, failures);
|
|
432
|
+
}
|
|
427
433
|
return {
|
|
428
434
|
operation: "migrate-verify",
|
|
429
435
|
status: failures.length ? "fail" : "pass",
|
|
@@ -437,3 +443,12 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
|
|
|
437
443
|
warnings,
|
|
438
444
|
};
|
|
439
445
|
}
|
|
446
|
+
function isRecord(value) {
|
|
447
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
448
|
+
}
|
|
449
|
+
function asRecord(value) {
|
|
450
|
+
return isRecord(value) ? value : {};
|
|
451
|
+
}
|
|
452
|
+
function errorMessage(error) {
|
|
453
|
+
return error instanceof Error ? error.message : "unknown parse error";
|
|
454
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
// Migration support scans dynamic target state until migration session domain types are modeled.
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import os from "node:os";
|
|
@@ -68,11 +67,12 @@ export function ensureSessionDir(projectName, requestedDir = "") {
|
|
|
68
67
|
return base;
|
|
69
68
|
}
|
|
70
69
|
export function statusCheckSummary(status) {
|
|
70
|
+
const legacy = status.checkState.legacy;
|
|
71
71
|
return {
|
|
72
72
|
status: status.checkState.status,
|
|
73
73
|
failures: status.checkState.failures,
|
|
74
74
|
warnings: status.checkState.warnings,
|
|
75
|
-
legacyStatus:
|
|
75
|
+
legacyStatus: legacy?.status || "skipped",
|
|
76
76
|
failureDetails: status.checkState.details.failures,
|
|
77
77
|
warningDetails: status.checkState.details.warnings,
|
|
78
78
|
};
|
|
@@ -162,8 +162,9 @@ export function validateFullCutoverSession(session, failures) {
|
|
|
162
162
|
}
|
|
163
163
|
if (summary.fullCutoverEligible !== true)
|
|
164
164
|
failures.push("full cutover requires summary.fullCutoverEligible=true");
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
const recommendedCapabilities = summary.recommendedCapabilities || [];
|
|
166
|
+
if (recommendedCapabilities.length) {
|
|
167
|
+
failures.push(`full cutover has recommended capabilities: ${recommendedCapabilities.join(", ")}`);
|
|
167
168
|
}
|
|
168
169
|
if (!session.target || !fs.existsSync(session.target))
|
|
169
170
|
return;
|