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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
- Document the Node.js 24+ runtime baseline as a release-significant change and
|
|
6
|
+
move the next publish line to `1.1.0` instead of another patch release.
|
|
7
|
+
- Add a `prepublishOnly` release gate that uses the source-safe dist bootstrap
|
|
8
|
+
to rebuild `dist/` and run the dist observation checker before `npm publish`.
|
|
9
|
+
- Make the source-safe `postinstall.mjs` bootstrap fail with the package-level
|
|
10
|
+
missing-dist message when installed package contents are incomplete, instead
|
|
11
|
+
of attempting a source checkout build script that is not shipped.
|
|
12
|
+
|
|
13
|
+
## 1.0.8
|
|
14
|
+
|
|
15
|
+
- Preserve the executable bit for the packaged `dist/harness.mjs` npm bin
|
|
16
|
+
entry during dist builds and prepack.
|
|
17
|
+
- Extend release observation checks to fail if the packed or installed
|
|
18
|
+
`harness` bin is not executable.
|
|
19
|
+
- Add `migrate-structure --plan` to the dist and installed-package command
|
|
20
|
+
smoke matrix.
|
|
21
|
+
|
|
22
|
+
## 1.0.7
|
|
23
|
+
|
|
24
|
+
- Generate `harness init --add-npm-scripts` commands with
|
|
25
|
+
`npx --yes coding-agent-harness ...` so target projects can run Harness
|
|
26
|
+
scripts without adding a project dependency or relying on a global install.
|
|
27
|
+
- Isolate the dist observation command matrix from local ignored docs by using
|
|
28
|
+
the minimal target-project fixture for target-facing commands.
|
|
29
|
+
- Preserve the executable bit on the committed `dist/harness.mjs` CLI entry.
|
|
30
|
+
|
|
31
|
+
## 1.0.6
|
|
32
|
+
|
|
33
|
+
- Bump the npm package version after the 1.0.5 publication so the TypeScript
|
|
34
|
+
runtime-source migration can be published again.
|
|
35
|
+
|
|
3
36
|
## 1.0.5
|
|
4
37
|
|
|
5
38
|
- Relicense the public package from MIT to AGPL-3.0-or-later.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -4,8 +4,8 @@ Thanks for helping improve Coding Agent Harness. This repository contains the pu
|
|
|
4
4
|
|
|
5
5
|
## Before You Start
|
|
6
6
|
|
|
7
|
-
- Use Node.js
|
|
8
|
-
- Install root dependencies with `npm install` from the repository root.
|
|
7
|
+
- Use Node.js 24 or newer. CI should run on the minimum supported line.
|
|
8
|
+
- Install root dependencies with `npm install` from the repository root. The install lifecycle generates the local `dist/` runtime; `dist/` is not tracked in Git.
|
|
9
9
|
- If you change `harness-gui`, also run `npm ci` inside `harness-gui/`.
|
|
10
10
|
- Keep pull requests focused. Separate documentation, CLI/runtime, template, preset, and GUI work when the changes are independent.
|
|
11
11
|
|
|
@@ -14,6 +14,7 @@ Thanks for helping improve Coding Agent Harness. This repository contains the pu
|
|
|
14
14
|
| Path | Purpose |
|
|
15
15
|
| --- | --- |
|
|
16
16
|
| `scripts/` | Public CLI and implementation modules. |
|
|
17
|
+
| `dist/` | Generated runtime output. It is published to npm but ignored in Git. |
|
|
17
18
|
| `tests/` | Root package tests and dashboard smoke tests. |
|
|
18
19
|
| `templates/`, `templates-zh-CN/` | Harness templates installed into target projects. |
|
|
19
20
|
| `presets/` | Bundled Harness preset packages. |
|
|
@@ -39,8 +40,8 @@ docs row. For larger PRs or when you are unsure, run the full root suite.
|
|
|
39
40
|
| Change type | Minimum local checks |
|
|
40
41
|
| --- | --- |
|
|
41
42
|
| Docs only | `git diff --check` |
|
|
42
|
-
| CLI/runtime | `npm test`, `npm run check`, `git diff --check` |
|
|
43
|
-
| Templates or examples | `npm test`, `node dist/harness.mjs check --profile target-project examples/minimal-project`, `git diff --check` |
|
|
43
|
+
| CLI/runtime | `npm run typecheck`, `npm run typecheck:guards`, `npm test`, `npm run check`, `git diff --check` |
|
|
44
|
+
| Templates or examples | `npm test`, `npm run build:runtime`, `node dist/harness.mjs check --profile target-project examples/minimal-project`, `git diff --check` |
|
|
44
45
|
| Dashboard | `npm test`, `npm run smoke:dashboard`, `git diff --check` |
|
|
45
46
|
| Package surface | `npm test`, `npm run pack:dry-run`, `git diff --check` |
|
|
46
47
|
| GUI submodule | `cd harness-gui && npm ci && npm run typecheck && npm test && npm run build` |
|
|
@@ -49,6 +50,9 @@ Full root suite:
|
|
|
49
50
|
|
|
50
51
|
```bash
|
|
51
52
|
npm install
|
|
53
|
+
npm run build:runtime
|
|
54
|
+
npm run typecheck
|
|
55
|
+
npm run typecheck:guards
|
|
52
56
|
npm test
|
|
53
57
|
npm run smoke:dashboard
|
|
54
58
|
npm run check
|
|
@@ -93,6 +97,6 @@ Use draft PRs for work that still needs design review, incomplete checks, or fol
|
|
|
93
97
|
|
|
94
98
|
## CI Expectations
|
|
95
99
|
|
|
96
|
-
GitHub Actions validates the root package, source/package boundary, minimal target project, dashboard smoke path, npm package dry run, and GUI submodule typecheck/test/build path. A local run should match the CI commands closely enough that failures are reproducible.
|
|
100
|
+
GitHub Actions validates the root TypeScript integrity gate (`npm run typecheck` and `npm run typecheck:guards`), root package tests, source/package boundary, minimal target project, dashboard smoke path, npm package dry run, and GUI submodule typecheck/test/build path. A local run should match the CI commands closely enough that failures are reproducible.
|
|
97
101
|
|
|
98
102
|
Repository owners may configure branch protection and required checks separately in GitHub. Contributors do not need to manage those settings.
|
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ English | [简体中文](README.zh-CN.md) | [日本語](docs-release/intl/ja-JP.
|
|
|
14
14
|
|
|
15
15
|
Coding Agent Harness is not another collection of chat prompts. It turns the durable facts that coding agents need into repository files: entry agreements, task plans, execution evidence, regression results, dashboards, and closeout records.
|
|
16
16
|
|
|
17
|
+
Requires Node.js 24 or newer.
|
|
18
|
+
|
|
17
19
|
The smallest loop is:
|
|
18
20
|
|
|
19
21
|
- A human states the goal, and the agent reads the repository Harness first.
|
|
@@ -96,6 +98,14 @@ runtime. Target projects should not treat `planning/**/_task-template` or
|
|
|
96
98
|
migration removes those generated legacy template directories when it finds
|
|
97
99
|
them.
|
|
98
100
|
|
|
101
|
+
Modules are registered in the root `harness.yaml` under `modules.items`.
|
|
102
|
+
`harness module register` creates only the module-owned `brief.md` and
|
|
103
|
+
`module_plan.md`, then regenerates `planning/modules/Module-Registry.md` as a
|
|
104
|
+
read-only view. Task execution files such as `execution_strategy.md`,
|
|
105
|
+
`visual_map.md`, `review.md`, and `walkthrough.md` are created under concrete
|
|
106
|
+
project or module task directories, including
|
|
107
|
+
`planning/modules/<key>/tasks/<task-id>/`.
|
|
108
|
+
|
|
99
109
|
### Safe Migration For Existing Projects
|
|
100
110
|
|
|
101
111
|
Legacy project migration starts with a scan, a migration plan, a recommended migration mode, and user confirmation. Only then should the agent write files. Final status is proven with a dashboard and checks.
|
|
@@ -340,7 +350,7 @@ When the migration is complete, report the dynamic workbench URL or static dashb
|
|
|
340
350
|
|
|
341
351
|
## Contributing
|
|
342
352
|
|
|
343
|
-
External contributors should start with [`CONTRIBUTING.md`](CONTRIBUTING.md). It covers repository layout, pull request expectations, root package checks, Dashboard smoke tests, npm package dry runs, and GUI submodule validation. The detailed public workflow also lives in [`docs-release/guides/contributing.md`](docs-release/guides/contributing.md).
|
|
353
|
+
External contributors should start with [`CONTRIBUTING.md`](CONTRIBUTING.md). It covers repository layout, pull request expectations, enforced TypeScript integrity checks, root package checks, Dashboard smoke tests, npm package dry runs, and GUI submodule validation. The detailed public workflow also lives in [`docs-release/guides/contributing.md`](docs-release/guides/contributing.md).
|
|
344
354
|
|
|
345
355
|
If you want your coding agent to make a contribution, send it this prompt:
|
|
346
356
|
|
|
@@ -351,7 +361,7 @@ Start from the latest main branch and create a new feature branch. Read README.m
|
|
|
351
361
|
|
|
352
362
|
Keep the change scoped. Use only public repository files and do not rely on maintainer-local state, hidden workflows, credentials, generated dashboards, temporary files, or ignored local-only files.
|
|
353
363
|
|
|
354
|
-
Run the checks that match the change. For docs-only changes, run git diff --check. For root package changes, run npm install, npm test, npm run smoke:dashboard, npm run check, node dist/harness.mjs check --profile target-project examples/minimal-project, npm run pack:dry-run, and git diff --check as relevant. If the change touches harness-gui, also run cd harness-gui && npm ci && npm run typecheck && npm test && npm run build.
|
|
364
|
+
Run the checks that match the change. For docs-only changes, run git diff --check. For root package changes, run npm install, npm run build:runtime, npm run typecheck, npm run typecheck:guards, npm test, npm run smoke:dashboard, npm run check, node dist/harness.mjs check --profile target-project examples/minimal-project, npm run pack:dry-run, and git diff --check as relevant. If the change touches harness-gui, also run cd harness-gui && npm ci && npm run typecheck && npm test && npm run build. The source repository ignores `dist/`; npm install, prepare, prepack, and the root npm scripts regenerate it when needed.
|
|
355
365
|
|
|
356
366
|
When done, summarize what changed, list verification results, call out any skipped checks with reasons, and prepare the PR using the repository template.
|
|
357
367
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
Coding Agent Harness 不是另一个聊天提示词集合。它把 Agent 长程开发需要依赖的事实沉淀到仓库:入口协议、任务计划、执行证据、回归结果、Dashboard 和收口记录。
|
|
16
16
|
|
|
17
|
+
需要 Node.js 24 或更新版本。
|
|
18
|
+
|
|
17
19
|
最小闭环是:
|
|
18
20
|
|
|
19
21
|
- 人提出目标,Agent 先读仓库里的 Harness 协议。
|
|
@@ -86,6 +88,12 @@ Harness 自带内置 Preset,`harness init` 会把它们 seed 到目标项目
|
|
|
86
88
|
默认任务模板和模块模板来自当前安装的 npm 包,在命令运行时读取。目标项目不应该把
|
|
87
89
|
`planning/**/_task-template` 或 `planning/**/_module-template` 当作活跃状态;v2 结构迁移发现这些旧生成模板目录时会直接清理。
|
|
88
90
|
|
|
91
|
+
模块统一注册在根 `harness.yaml` 的 `modules.items` 里。`harness module register`
|
|
92
|
+
只创建模块根目录拥有的 `brief.md` 和 `module_plan.md`,然后把
|
|
93
|
+
`planning/modules/Module-Registry.md` 重新生成为只读视图。`execution_strategy.md`、
|
|
94
|
+
`visual_map.md`、`review.md`、`walkthrough.md` 等执行合同属于具体任务目录,例如
|
|
95
|
+
`planning/modules/<key>/tasks/<task-id>/`。
|
|
96
|
+
|
|
89
97
|
### 旧项目也能迁移
|
|
90
98
|
|
|
91
99
|
旧项目迁移不是直接套模板。标准流程是:先扫描项目,生成迁移计划,推荐迁移模式,向用户提问确认,再执行迁移,最后用 Dashboard 和检查结果证明迁移状态。
|
|
@@ -320,7 +328,7 @@ npx --yes coding-agent-harness new-task --budget complex --preset legacy-migrati
|
|
|
320
328
|
|
|
321
329
|
## 参与贡献
|
|
322
330
|
|
|
323
|
-
外部贡献者请先阅读 [`CONTRIBUTING.md`](CONTRIBUTING.md)。它说明仓库结构、PR
|
|
331
|
+
外部贡献者请先阅读 [`CONTRIBUTING.md`](CONTRIBUTING.md)。它说明仓库结构、PR 要求、enforced TypeScript integrity 检查、根包检查、Dashboard smoke test、npm package dry run 和 GUI 子模块验证。中文详细流程见 [`docs-release/guides/contributing.zh-CN.md`](docs-release/guides/contributing.zh-CN.md)。
|
|
324
332
|
|
|
325
333
|
如果你想让自己的 Coding Agent 帮你改这个仓库,可以把下面这段发给它:
|
|
326
334
|
|
|
@@ -331,7 +339,7 @@ npx --yes coding-agent-harness new-task --budget complex --preset legacy-migrati
|
|
|
331
339
|
|
|
332
340
|
改动要保持聚焦。只使用公开仓库文件;不要依赖维护者本地状态、隐藏工作流、凭据、生成的 Dashboard、临时文件或被 ignore 的本地专用文件。
|
|
333
341
|
|
|
334
|
-
根据改动范围运行检查。仅文档改动至少运行 git diff --check。根包相关改动按需运行 npm install、npm test、npm run smoke:dashboard、npm run check、node dist/harness.mjs check --profile target-project examples/minimal-project、npm run pack:dry-run 和 git diff --check。如果改到 harness-gui,还要运行 cd harness-gui && npm ci && npm run typecheck && npm test && npm run build
|
|
342
|
+
根据改动范围运行检查。仅文档改动至少运行 git diff --check。根包相关改动按需运行 npm install、npm run build:runtime、npm run typecheck、npm run typecheck:guards、npm test、npm run smoke:dashboard、npm run check、node dist/harness.mjs check --profile target-project examples/minimal-project、npm run pack:dry-run 和 git diff --check。如果改到 harness-gui,还要运行 cd harness-gui && npm ci && npm run typecheck && npm test && npm run build。源码仓不跟踪 `dist/`;npm install、prepare、prepack 和根仓 npm scripts 会按需重新生成。
|
|
335
343
|
|
|
336
344
|
完成后,请总结改了什么,列出验证结果,说明任何未运行检查及原因,并按仓库 PR 模板准备 PR。
|
|
337
345
|
```
|
package/SKILL.md
CHANGED
|
@@ -31,6 +31,16 @@ description: >
|
|
|
31
31
|
- **严肃项目用顶级模型。** 便宜模型的返工成本远高于差价。
|
|
32
32
|
- **强制流程优于口头约定。** 每个步骤都应该是 agent 可自主执行的。
|
|
33
33
|
|
|
34
|
+
## Product Contract
|
|
35
|
+
|
|
36
|
+
Full Harness 与后续 Lite Skill 的共享任务语义由 Document Contract Kernel
|
|
37
|
+
约束,公开源位于 `docs-release/architecture/document-contract-kernel/README.md`。
|
|
38
|
+
凡是改变 AGENTS、context、task package、progress、walkthrough、review、
|
|
39
|
+
regression 或 lessons 基础语义的改动,必须按该 contract 同步 Full/Lite
|
|
40
|
+
overlay 与 compatibility matrix;Full-only 的 CLI、Dashboard、Preset、
|
|
41
|
+
generated ledger、Module Registry、lifecycle command、runtime requirement
|
|
42
|
+
不得泄漏进 Lite source。
|
|
43
|
+
|
|
34
44
|
---
|
|
35
45
|
|
|
36
46
|
## 主执行 SOP
|
|
@@ -64,7 +74,8 @@ CLI 示例默认使用目标项目可调用的 `harness` 命令。执行前先
|
|
|
64
74
|
`command -v harness`;如果没有,不要静默全局安装,按安装指南询问用户是否
|
|
65
75
|
允许 `npm install -g coding-agent-harness`。未获明确同意时,用
|
|
66
76
|
`npx --yes coding-agent-harness <command>` 执行同一条命令。只有维护本源码
|
|
67
|
-
checkout 时,才把 `harness` 替换为 `node dist/harness.mjs
|
|
77
|
+
checkout 时,才把 `harness` 替换为 `node dist/harness.mjs`;源码仓的
|
|
78
|
+
`dist/` 是生成物,若不存在先运行 `npm install` 或 `npm run build:runtime`。
|
|
68
79
|
|
|
69
80
|
### Agent 安装合同
|
|
70
81
|
|
|
@@ -252,7 +263,7 @@ harness bootstrap 完成后,项目中至少应存在以下文件:
|
|
|
252
263
|
- [ ] `coding-agent-harness/governance/standards/walkthrough-template.md`
|
|
253
264
|
- [ ] `coding-agent-harness/governance/generated/Closeout-Index.md`
|
|
254
265
|
- [ ] `coding-agent-harness/governance/lessons/`(空目录 + .gitkeep)
|
|
255
|
-
- [ ] `coding-agent-harness/governance/
|
|
266
|
+
- [ ] `coding-agent-harness/governance/archive/`(空目录 + .gitkeep)
|
|
256
267
|
- [ ] `coding-agent-harness/governance/generated/Harness-Ledger.md`
|
|
257
268
|
- [ ] `coding-agent-harness/governance/standards/external-source-intake-standard.md`
|
|
258
269
|
- [ ] `coding-agent-harness/governance/standards/harness-ledger-standard.md`
|
|
@@ -339,7 +350,7 @@ harness 搭建完成后,每个 feature 从想法到代码的标准流程:
|
|
|
339
350
|
| Long-Running Task Contract | `templates/planning/long-running-task-contract.md` | 长程任务授权、review loop 和停止条件 |
|
|
340
351
|
| Module Session Prompt | `templates/planning/module_session_prompt.md` | 模块并行开发会话冷启动 |
|
|
341
352
|
| Walkthrough | `templates/walkthrough/walkthrough-template.md` | 任务收口记录 |
|
|
342
|
-
| Closeout Index | `
|
|
353
|
+
| Closeout Index | `governance rebuild` generated output | `governance/generated/Closeout-Index.md` 由 task closeout metadata 重建,无独立模板 |
|
|
343
354
|
| Testing Standard | `templates/reference/testing-standard.md` | 测试、冒烟和回归规范 |
|
|
344
355
|
| Execution Workflow | `templates/reference/execution-workflow-standard.md` | 执行、提交、PR 和证据记录 |
|
|
345
356
|
| Delivery Operating Model Standard | `templates/reference/delivery-operating-model-standard.md` | 交付组织模型选择 |
|
package/dist/build-dist.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// @ts-nocheck
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import os from "node:os";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import { spawnSync } from "node:child_process";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
7
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
8
|
const typescriptVersion = "5.9.3";
|
|
10
9
|
export function buildRuntimeDist({ projectRoot = repoRoot, configPath = path.join(projectRoot, "tsconfig.dist.json"), outDir = path.join(projectRoot, "dist"), } = {}) {
|
|
@@ -28,14 +27,18 @@ export function buildRuntimeDist({ projectRoot = repoRoot, configPath = path.joi
|
|
|
28
27
|
};
|
|
29
28
|
}
|
|
30
29
|
fs.rmSync(buildOutDir, { recursive: true, force: true });
|
|
31
|
-
const
|
|
30
|
+
const npmArgs = ["exec", "--yes", "--package", `typescript@${typescriptVersion}`, "--", "tsc", "-p", absoluteConfig, "--outDir", buildOutDir, "--noCheck"];
|
|
31
|
+
const npmExecPath = process.env.npm_execpath;
|
|
32
|
+
const command = npmExecPath ? process.execPath : process.platform === "win32" ? "npm.cmd" : "npm";
|
|
33
|
+
const commandArgs = npmExecPath ? [npmExecPath, ...npmArgs] : npmArgs;
|
|
34
|
+
const emit = spawnSync(command, commandArgs, {
|
|
32
35
|
cwd: absoluteProjectRoot,
|
|
33
36
|
encoding: "utf8",
|
|
34
37
|
});
|
|
35
38
|
if (emit.status !== 0) {
|
|
36
39
|
return {
|
|
37
40
|
ok: false,
|
|
38
|
-
error: `TypeScript dist build failed\nSTDOUT:\n${emit.stdout}\nSTDERR:\n${emit.stderr}`,
|
|
41
|
+
error: `TypeScript dist build failed${emit.error ? `\nERROR:\n${emit.error.message}` : ""}\nSTDOUT:\n${emit.stdout ?? ""}\nSTDERR:\n${emit.stderr ?? ""}`,
|
|
39
42
|
status: emit.status,
|
|
40
43
|
};
|
|
41
44
|
}
|
|
@@ -43,6 +46,10 @@ export function buildRuntimeDist({ projectRoot = repoRoot, configPath = path.joi
|
|
|
43
46
|
syncDirectory(buildOutDir, absoluteOutDir);
|
|
44
47
|
fs.rmSync(buildOutDir, { recursive: true, force: true });
|
|
45
48
|
}
|
|
49
|
+
restoreExecutableEntrypoints({
|
|
50
|
+
outDir: absoluteOutDir,
|
|
51
|
+
binRelativePaths: ["harness.mjs"],
|
|
52
|
+
});
|
|
46
53
|
const files = collectFiles(absoluteOutDir).filter((file) => file.endsWith(".mjs")).sort();
|
|
47
54
|
const relativeFiles = files.map((file) => toPosix(path.relative(absoluteOutDir, file)));
|
|
48
55
|
const requiredFiles = [
|
|
@@ -126,6 +133,15 @@ function syncDirectory(sourceDir, targetDir) {
|
|
|
126
133
|
fs.rmSync(path.join(targetDir, entry), { recursive: true, force: true });
|
|
127
134
|
}
|
|
128
135
|
}
|
|
136
|
+
function restoreExecutableEntrypoints({ outDir, binRelativePaths }) {
|
|
137
|
+
for (const relativePath of binRelativePaths) {
|
|
138
|
+
const file = path.join(outDir, relativePath);
|
|
139
|
+
if (!fs.existsSync(file))
|
|
140
|
+
continue;
|
|
141
|
+
const stat = fs.statSync(file);
|
|
142
|
+
fs.chmodSync(file, stat.mode | 0o755);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
129
145
|
function toPosix(value) {
|
|
130
146
|
return value.split(path.sep).join("/");
|
|
131
147
|
}
|
|
@@ -165,13 +181,23 @@ function requireValue(argv, index, option) {
|
|
|
165
181
|
throw new Error(`${option} requires a value`);
|
|
166
182
|
return value;
|
|
167
183
|
}
|
|
168
|
-
|
|
184
|
+
function isMainModule() {
|
|
185
|
+
if (!process.argv[1])
|
|
186
|
+
return false;
|
|
187
|
+
try {
|
|
188
|
+
return fs.realpathSync.native(fileURLToPath(import.meta.url)) === fs.realpathSync.native(process.argv[1]);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (isMainModule()) {
|
|
169
195
|
let options;
|
|
170
196
|
try {
|
|
171
197
|
options = parseArgs(process.argv.slice(2));
|
|
172
198
|
}
|
|
173
199
|
catch (error) {
|
|
174
|
-
console.error(error.message);
|
|
200
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
175
201
|
process.exit(1);
|
|
176
202
|
}
|
|
177
203
|
const result = buildRuntimeDist(options);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// @ts-nocheck
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import os from "node:os";
|
|
5
4
|
import path from "node:path";
|
|
@@ -21,16 +20,18 @@ export function checkDistObservation({ projectRoot = defaultProjectRoot, runPack
|
|
|
21
20
|
return { ok: false, failures, observations };
|
|
22
21
|
expectEqual(failures, "package-bin-not-dist", pkg.bin?.harness, "dist/harness.mjs", "package bin.harness must resolve to dist/harness.mjs");
|
|
23
22
|
const distRuntimeScripts = {
|
|
24
|
-
check: "
|
|
25
|
-
"check:private": "
|
|
26
|
-
status: "
|
|
27
|
-
dashboard: "
|
|
28
|
-
"dashboard:folder": "
|
|
29
|
-
postinstall: "
|
|
30
|
-
|
|
23
|
+
check: ["run-dist.mjs", "harness.mjs", "check", "--profile", "source-package"],
|
|
24
|
+
"check:private": ["run-dist.mjs", "harness.mjs", "check", "--profile", "private-harness"],
|
|
25
|
+
status: ["run-dist.mjs", "harness.mjs", "status", "--json"],
|
|
26
|
+
dashboard: ["run-dist.mjs", "harness.mjs", "dashboard", "--out"],
|
|
27
|
+
"dashboard:folder": ["run-dist.mjs", "harness.mjs", "dashboard", "--out-dir"],
|
|
28
|
+
postinstall: ["postinstall.mjs"],
|
|
29
|
+
prepare: ["postinstall.mjs", "--build-only"],
|
|
30
|
+
prepublishOnly: ["run-dist.mjs", "check-dist-observation.mjs", "--skip-install-smoke"],
|
|
31
|
+
"observe:dist": ["run-dist.mjs", "check-dist-observation.mjs", "--skip-pack", "--skip-install-smoke"],
|
|
31
32
|
};
|
|
32
|
-
for (const [name,
|
|
33
|
-
|
|
33
|
+
for (const [name, tokens] of Object.entries(distRuntimeScripts)) {
|
|
34
|
+
expectScriptIncludes(failures, `package-script-${name}-not-source-safe-dist`, pkg.scripts?.[name], tokens, `package script ${name} must run through the source-safe dist bootstrap`);
|
|
34
35
|
}
|
|
35
36
|
observations.packageRuntime = {
|
|
36
37
|
bin: pkg.bin?.harness,
|
|
@@ -48,20 +49,30 @@ export function checkDistObservation({ projectRoot = defaultProjectRoot, runPack
|
|
|
48
49
|
failures.push({ code: "pack-dry-run-failed", message: `npm pack dry-run failed\nSTDOUT:\n${pack.stdout}\nSTDERR:\n${pack.stderr}` });
|
|
49
50
|
}
|
|
50
51
|
else {
|
|
51
|
-
const
|
|
52
|
+
const packedEntries = JSON.parse(pack.stdout)[0]?.files ?? [];
|
|
53
|
+
const packed = packedEntries.map((file) => file.path).sort();
|
|
54
|
+
const packedModeByPath = new Map(packedEntries.map((file) => [file.path, file.mode]));
|
|
55
|
+
const distHarnessMode = packedModeByPath.get("dist/harness.mjs");
|
|
52
56
|
observations.package = {
|
|
53
57
|
entryCount: packed.length,
|
|
54
58
|
hasDistHarness: packed.includes("dist/harness.mjs"),
|
|
55
59
|
hasDistPostinstall: packed.includes("dist/postinstall.mjs"),
|
|
56
60
|
hasDistObservationGate: packed.includes("dist/check-dist-observation.mjs"),
|
|
61
|
+
hasPostinstallBootstrap: packed.includes("postinstall.mjs"),
|
|
62
|
+
hasRunDistBootstrap: packed.includes("run-dist.mjs"),
|
|
57
63
|
hasScriptsHarness: packed.includes("scripts/harness.mjs"),
|
|
58
64
|
hasScripts: packed.some((file) => file.startsWith("scripts/")),
|
|
59
65
|
hasTests: packed.some((file) => file.startsWith("tests/")),
|
|
66
|
+
distHarnessMode,
|
|
67
|
+
distHarnessExecutable: typeof distHarnessMode === "number" && Boolean(distHarnessMode & 0o111),
|
|
60
68
|
};
|
|
61
|
-
for (const required of ["dist/harness.mjs", "dist/postinstall.mjs", "dist/check-dist-observation.mjs"]) {
|
|
69
|
+
for (const required of ["postinstall.mjs", "run-dist.mjs", "dist/harness.mjs", "dist/postinstall.mjs", "dist/check-dist-observation.mjs"]) {
|
|
62
70
|
if (!packed.includes(required))
|
|
63
71
|
failures.push({ code: "packed-file-missing", file: required, message: `package missing ${required}` });
|
|
64
72
|
}
|
|
73
|
+
if (!observations.package.distHarnessExecutable) {
|
|
74
|
+
failures.push({ code: "packed-bin-not-executable", file: "dist/harness.mjs", mode: distHarnessMode, message: "package bin dist/harness.mjs must be executable" });
|
|
75
|
+
}
|
|
65
76
|
if (observations.package.hasScripts)
|
|
66
77
|
failures.push({ code: "package-includes-scripts", message: "package must not include scripts/** after historical shim deletion" });
|
|
67
78
|
if (observations.package.hasTests)
|
|
@@ -133,6 +144,7 @@ function runMatrix(root, failures, commandMatrix) {
|
|
|
133
144
|
{ id: "source-check", args: ["check", "--profile", "source-package", "."] },
|
|
134
145
|
{ id: "target-check", args: ["check", "--profile", "target-project", "examples/minimal-project"] },
|
|
135
146
|
{ id: "migrate-plan", args: ["migrate-plan", "--json", "--limit", "20", "examples/minimal-project"] },
|
|
147
|
+
{ id: "migrate-structure-plan", args: ["migrate-structure", "--plan", "--json", "examples/minimal-project"] },
|
|
136
148
|
{ id: "dashboard", args: ["dashboard", "--out-dir", path.join("tmp", `pr-27-observation-dashboard-${process.pid}`), "examples/minimal-project"] },
|
|
137
149
|
];
|
|
138
150
|
for (const entry of matrix) {
|
|
@@ -193,7 +205,12 @@ function runInstalledPackageSmoke(root, failures, observations) {
|
|
|
193
205
|
failures.push({ code: "install-smoke-pack-failed", message: `npm pack failed\nSTDOUT:\n${pack.stdout}\nSTDERR:\n${pack.stderr}` });
|
|
194
206
|
return;
|
|
195
207
|
}
|
|
196
|
-
const
|
|
208
|
+
const tarballName = pack.stdout.trim().split(/\r?\n/).at(-1);
|
|
209
|
+
if (!tarballName) {
|
|
210
|
+
failures.push({ code: "install-smoke-pack-empty", message: "npm pack did not print a tarball name" });
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const tarball = path.join(packDir, tarballName);
|
|
197
214
|
fs.writeFileSync(path.join(consumer, "package.json"), JSON.stringify({ private: true, type: "module" }, null, 2));
|
|
198
215
|
const install = spawnSync("npm", ["install", "--silent", "--no-audit", "--no-fund", tarball], {
|
|
199
216
|
cwd: consumer,
|
|
@@ -211,11 +228,15 @@ function runInstalledPackageSmoke(root, failures, observations) {
|
|
|
211
228
|
if (!pkg)
|
|
212
229
|
return;
|
|
213
230
|
const binTarget = fs.existsSync(bin) ? fs.readlinkSync(bin) : "";
|
|
214
|
-
|
|
231
|
+
const installedBinFile = path.join(packageRoot, "dist/harness.mjs");
|
|
232
|
+
const installedBinMode = fs.existsSync(installedBinFile) ? fs.statSync(installedBinFile).mode : undefined;
|
|
233
|
+
const installSmoke = {
|
|
215
234
|
nodeVersion,
|
|
216
235
|
tempRoot,
|
|
217
236
|
binTarget,
|
|
218
237
|
bin: pkg.bin?.harness,
|
|
238
|
+
binMode: installedBinMode,
|
|
239
|
+
binExecutable: typeof installedBinMode === "number" && Boolean(installedBinMode & 0o111),
|
|
219
240
|
postinstall: pkg.scripts?.postinstall,
|
|
220
241
|
observeDist: pkg.scripts?.["observe:dist"],
|
|
221
242
|
hasTests: fs.existsSync(path.join(packageRoot, "tests")),
|
|
@@ -223,48 +244,60 @@ function runInstalledPackageSmoke(root, failures, observations) {
|
|
|
223
244
|
scriptsDisabled: [],
|
|
224
245
|
steps: [],
|
|
225
246
|
};
|
|
247
|
+
observations.installSmoke = installSmoke;
|
|
226
248
|
expectEqual(failures, "installed-bin-not-dist", pkg.bin?.harness, "dist/harness.mjs", "installed package bin.harness must resolve to dist/harness.mjs");
|
|
227
|
-
|
|
228
|
-
|
|
249
|
+
expectScriptIncludes(failures, "installed-postinstall-not-source-safe", pkg.scripts?.postinstall, ["postinstall.mjs"], "installed package postinstall must use the source-safe bootstrap");
|
|
250
|
+
expectScriptIncludes(failures, "installed-observe-dist-not-source-safe", pkg.scripts?.["observe:dist"], ["run-dist.mjs", "check-dist-observation.mjs"], "installed observe:dist must use the source-safe dist bootstrap");
|
|
229
251
|
if (!binTarget.includes("dist/harness.mjs")) {
|
|
230
252
|
failures.push({ code: "installed-bin-link-not-dist", message: `installed bin link does not target dist/harness.mjs: ${binTarget}` });
|
|
231
253
|
}
|
|
232
|
-
|
|
254
|
+
if (!installSmoke.binExecutable) {
|
|
255
|
+
failures.push({ code: "installed-bin-not-executable", file: "dist/harness.mjs", mode: installedBinMode, message: "installed package bin dist/harness.mjs must be executable" });
|
|
256
|
+
}
|
|
257
|
+
for (const relative of ["postinstall.mjs", "run-dist.mjs", "dist/harness.mjs", "dist/postinstall.mjs", "dist/check-dist-observation.mjs"]) {
|
|
233
258
|
if (!fs.existsSync(path.join(packageRoot, relative)))
|
|
234
259
|
failures.push({ code: "installed-file-missing", file: relative, message: `installed package missing ${relative}` });
|
|
235
260
|
}
|
|
236
|
-
if (
|
|
261
|
+
if (installSmoke.hasTests)
|
|
237
262
|
failures.push({ code: "installed-package-includes-tests", message: "installed package must not include tests/**" });
|
|
238
|
-
if (
|
|
263
|
+
if (installSmoke.hasScripts)
|
|
239
264
|
failures.push({ code: "installed-package-includes-scripts", message: "installed package must not include scripts/** after historical shim deletion" });
|
|
240
265
|
const installedScripts = path.join(packageRoot, "scripts");
|
|
241
266
|
if (fs.existsSync(installedScripts)) {
|
|
242
267
|
fs.renameSync(installedScripts, `${installedScripts}.disabled-by-dist-observation`);
|
|
243
|
-
|
|
268
|
+
installSmoke.scriptsDisabled.push("scripts/");
|
|
244
269
|
}
|
|
245
270
|
const runtimeEnv = isolatedEnv({ nodeBin, home, extraPath: [path.join(consumer, "node_modules", ".bin")] });
|
|
246
|
-
runInstalledMatrix(root, runtimeEnv, failures,
|
|
271
|
+
runInstalledMatrix(root, runtimeEnv, failures, installSmoke.steps);
|
|
247
272
|
const postinstall = spawnSync(node24, [path.join(packageRoot, "dist/postinstall.mjs")], {
|
|
248
273
|
cwd: packageRoot,
|
|
249
274
|
encoding: "utf8",
|
|
250
275
|
env: { ...runtimeEnv, CODING_AGENT_HARNESS_SKIP_POSTINSTALL: "1" },
|
|
251
276
|
});
|
|
252
|
-
|
|
277
|
+
installSmoke.steps.push({ id: "installed-dist-postinstall", status: postinstall.status });
|
|
253
278
|
if (postinstall.status !== 0)
|
|
254
279
|
failures.push({ code: "installed-postinstall-failed", message: `installed dist postinstall failed\nSTDOUT:\n${postinstall.stdout}\nSTDERR:\n${postinstall.stderr}` });
|
|
280
|
+
const postinstallBootstrap = spawnSync(node24, [path.join(packageRoot, "postinstall.mjs")], {
|
|
281
|
+
cwd: packageRoot,
|
|
282
|
+
encoding: "utf8",
|
|
283
|
+
env: { ...runtimeEnv, CODING_AGENT_HARNESS_SKIP_POSTINSTALL: "1" },
|
|
284
|
+
});
|
|
285
|
+
installSmoke.steps.push({ id: "installed-postinstall-bootstrap", status: postinstallBootstrap.status });
|
|
286
|
+
if (postinstallBootstrap.status !== 0)
|
|
287
|
+
failures.push({ code: "installed-postinstall-bootstrap-failed", message: `installed postinstall bootstrap failed\nSTDOUT:\n${postinstallBootstrap.stdout}\nSTDERR:\n${postinstallBootstrap.stderr}` });
|
|
255
288
|
const installedObservation = spawnSync(node24, [path.join(packageRoot, "dist/check-dist-observation.mjs"), "--project-root", packageRoot, "--skip-pack", "--skip-install-smoke", "--skip-command-matrix", "--json"], {
|
|
256
289
|
cwd: packageRoot,
|
|
257
290
|
encoding: "utf8",
|
|
258
291
|
env: runtimeEnv,
|
|
259
292
|
maxBuffer: 32 * 1024 * 1024,
|
|
260
293
|
});
|
|
261
|
-
|
|
294
|
+
installSmoke.steps.push({ id: "installed-observation", status: installedObservation.status });
|
|
262
295
|
if (installedObservation.status !== 0) {
|
|
263
296
|
failures.push({ code: "installed-observation-failed", message: `installed observation failed\nSTDOUT:\n${installedObservation.stdout}\nSTDERR:\n${installedObservation.stderr}` });
|
|
264
297
|
}
|
|
265
298
|
else {
|
|
266
299
|
const installedResult = JSON.parse(installedObservation.stdout);
|
|
267
|
-
|
|
300
|
+
installSmoke.observationOk = installedResult.ok;
|
|
268
301
|
if (!installedResult.ok)
|
|
269
302
|
failures.push({ code: "installed-observation-not-ok", message: JSON.stringify(installedResult.failures, null, 2) });
|
|
270
303
|
}
|
|
@@ -278,6 +311,7 @@ function runInstalledMatrix(root, runtimeEnv, failures, steps) {
|
|
|
278
311
|
{ id: "installed-source-check", cwd: root, args: ["check", "--profile", "source-package", "."] },
|
|
279
312
|
{ id: "installed-target-check", cwd: root, args: ["check", "--profile", "target-project", "examples/minimal-project"] },
|
|
280
313
|
{ id: "installed-migrate-plan", cwd: root, args: ["migrate-plan", "--json", "--limit", "20", "examples/minimal-project"] },
|
|
314
|
+
{ id: "installed-migrate-structure-plan", cwd: root, args: ["migrate-structure", "--plan", "--json", "examples/minimal-project"] },
|
|
281
315
|
{ id: "installed-dashboard", cwd: root, args: ["dashboard", "--out-dir", path.join("tmp", `pr-27-installed-observation-dashboard-${process.pid}`), "examples/minimal-project"] },
|
|
282
316
|
];
|
|
283
317
|
for (const entry of matrix) {
|
|
@@ -306,7 +340,7 @@ function findNode24() {
|
|
|
306
340
|
path.join(os.homedir(), ".nvm", "versions", "node", "v24.13.1", "bin", "node"),
|
|
307
341
|
"/opt/homebrew/opt/node@24/bin/node",
|
|
308
342
|
"/usr/local/opt/node@24/bin/node",
|
|
309
|
-
].filter(
|
|
343
|
+
].filter(isNonEmptyString);
|
|
310
344
|
for (const candidate of candidates) {
|
|
311
345
|
if (!fs.existsSync(candidate))
|
|
312
346
|
continue;
|
|
@@ -316,7 +350,10 @@ function findNode24() {
|
|
|
316
350
|
}
|
|
317
351
|
return undefined;
|
|
318
352
|
}
|
|
319
|
-
function
|
|
353
|
+
function isNonEmptyString(value) {
|
|
354
|
+
return typeof value === "string" && value.length > 0;
|
|
355
|
+
}
|
|
356
|
+
function isolatedEnv({ nodeBin, home = process.env.HOME || os.homedir(), extraPath = [], }) {
|
|
320
357
|
return {
|
|
321
358
|
...process.env,
|
|
322
359
|
HOME: home,
|
|
@@ -329,14 +366,22 @@ function readJson(file, failures, code) {
|
|
|
329
366
|
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
330
367
|
}
|
|
331
368
|
catch (error) {
|
|
332
|
-
failures.push({ code, message: `failed to read ${file}: ${error
|
|
369
|
+
failures.push({ code, message: `failed to read ${file}: ${errorMessage(error)}` });
|
|
333
370
|
return undefined;
|
|
334
371
|
}
|
|
335
372
|
}
|
|
373
|
+
function errorMessage(error) {
|
|
374
|
+
return error instanceof Error ? error.message : String(error);
|
|
375
|
+
}
|
|
336
376
|
function expectEqual(failures, code, actual, expected, message) {
|
|
337
377
|
if (actual !== expected)
|
|
338
378
|
failures.push({ code, actual, expected, message });
|
|
339
379
|
}
|
|
380
|
+
function expectScriptIncludes(failures, code, script, tokens, message) {
|
|
381
|
+
if (typeof script !== "string" || tokens.some((token) => !script.includes(token)) || script.includes("scripts/")) {
|
|
382
|
+
failures.push({ code, actual: script, expectedTokens: tokens, message });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
340
385
|
function parseImportSpecifiers(content) {
|
|
341
386
|
const specifiers = [];
|
|
342
387
|
for (const match of content.matchAll(/\bfrom\s*["']([^"']+)["']/g))
|
|
@@ -413,7 +458,7 @@ if (isMainModule()) {
|
|
|
413
458
|
options = parseArgs(process.argv.slice(2));
|
|
414
459
|
}
|
|
415
460
|
catch (error) {
|
|
416
|
-
console.error(error
|
|
461
|
+
console.error(errorMessage(error));
|
|
417
462
|
process.exit(1);
|
|
418
463
|
}
|
|
419
464
|
const result = checkDistObservation(options);
|