@polymorphism-tech/morph-spec 9.0.0 → 9.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.
Files changed (304) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +4 -62
  4. package/bin/validate.js +1 -39
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +1 -1
  9. package/framework/MORPH.md +1 -1
  10. package/framework/agents.json +12 -4
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +6 -17
  14. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  15. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  16. package/framework/hooks/claude-code/core/chain.js +4 -65
  17. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  18. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  19. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  20. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  21. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  22. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  23. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  24. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  25. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  26. package/framework/hooks/claude-code/core/state-sync.js +11 -67
  27. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  28. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  29. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  30. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  31. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  32. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  33. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  34. package/framework/hooks/shared/activity-logger.js +9 -89
  35. package/framework/hooks/shared/agent-reap.js +2 -17
  36. package/framework/hooks/shared/build-check.js +6 -75
  37. package/framework/hooks/shared/claim-registry.js +9 -119
  38. package/framework/hooks/shared/command-tokens.js +6 -51
  39. package/framework/hooks/shared/comment-rules.js +5 -20
  40. package/framework/hooks/shared/comment-scanner.js +6 -25
  41. package/framework/hooks/shared/comment-stripper.js +11 -85
  42. package/framework/hooks/shared/e2e-check.js +0 -0
  43. package/framework/hooks/shared/errata.js +2 -15
  44. package/framework/hooks/shared/feature-locator.js +11 -95
  45. package/framework/hooks/shared/frontend-locations.js +1 -3
  46. package/framework/hooks/shared/gate-question.js +1 -7
  47. package/framework/hooks/shared/git-exclude.js +3 -20
  48. package/framework/hooks/shared/git-paths.js +8 -104
  49. package/framework/hooks/shared/graph-freshness.js +22 -166
  50. package/framework/hooks/shared/graph-refresh.js +21 -149
  51. package/framework/hooks/shared/heavy-command.js +4 -12
  52. package/framework/hooks/shared/hook-response.js +8 -83
  53. package/framework/hooks/shared/morph-cli.js +8 -52
  54. package/framework/hooks/shared/payload-utils.js +10 -132
  55. package/framework/hooks/shared/phase-utils.js +1 -42
  56. package/framework/hooks/shared/redact-secrets.js +4 -15
  57. package/framework/hooks/shared/reparse-scan.js +6 -66
  58. package/framework/hooks/shared/slice-progress.js +4 -23
  59. package/framework/hooks/shared/source-files.js +4 -17
  60. package/framework/hooks/shared/state-reader.js +13 -183
  61. package/framework/hooks/shared/state-writer.js +8 -52
  62. package/framework/hooks/shared/stdin-reader.js +1 -11
  63. package/framework/hooks/shared/telemetry-logger.js +7 -67
  64. package/framework/hooks/shared/transcript-usage.js +7 -29
  65. package/framework/hooks/shared/worktree-root.js +7 -29
  66. package/framework/rules/csharp-standards.md +1 -1
  67. package/framework/rules/frontend-standards.md +1 -0
  68. package/framework/rules/hangfire-standards.md +1 -1
  69. package/framework/rules/neon-standards.md +1 -1
  70. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  71. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  72. package/framework/skills/core/morph-apply/SKILL.md +2 -2
  73. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  74. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  75. package/framework/skills/core/morph-media/SKILL.md +1 -1
  76. package/framework/skills/core/morph-plan/SKILL.md +2 -2
  77. package/framework/skills/core/morph-review/SKILL.md +1 -1
  78. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  79. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  80. package/framework/standards/STANDARDS.json +34 -12
  81. package/framework/standards/STANDARDS.meta.json +16 -2
  82. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  83. package/framework/standards/ai-agents/agent-session.md +1 -1
  84. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  85. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  86. package/framework/standards/ai-agents/guardrails.md +1 -1
  87. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  88. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  89. package/framework/standards/ai-agents/production.md +1 -1
  90. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  91. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  92. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  93. package/framework/standards/ai-agents/setup.md +2 -2
  94. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  95. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  96. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  97. package/framework/standards/ai-agents/workflows.md +1 -1
  98. package/framework/standards/architecture/code-comments.md +1 -1
  99. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  100. package/framework/standards/backend/api/validation.md +1 -1
  101. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  102. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  103. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  104. package/framework/standards/data/nosql/cache/redis.md +1 -1
  105. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  106. package/framework/standards/frontend/admin-app/forms.md +1 -1
  107. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  108. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  109. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  110. package/framework/templates/CLAUDE.md.core.template +3 -3
  111. package/framework/templates/REGISTRY.json +9 -9
  112. package/framework/templates/docs/ui-mockups.md +4 -0
  113. package/package.json +1 -1
  114. package/src/commands/context/context-refresh.js +0 -16
  115. package/src/commands/dashboard/dashboard.js +2 -8
  116. package/src/commands/e2e/e2e.js +17 -104
  117. package/src/commands/graph/affected.js +0 -14
  118. package/src/commands/graph/build.js +11 -50
  119. package/src/commands/graph/explain.js +0 -16
  120. package/src/commands/graph/status.js +1 -12
  121. package/src/commands/mcp/mcp-setup.js +4 -62
  122. package/src/commands/observability/cost.js +5 -57
  123. package/src/commands/observability/telemetry.js +1 -14
  124. package/src/commands/project/branch.js +4 -45
  125. package/src/commands/project/claim.js +4 -48
  126. package/src/commands/project/doctor.js +48 -302
  127. package/src/commands/project/env.js +1 -27
  128. package/src/commands/project/fleet.js +3 -32
  129. package/src/commands/project/init.js +4 -68
  130. package/src/commands/project/install-plugin-cmd.js +0 -1
  131. package/src/commands/project/standards.js +1 -62
  132. package/src/commands/project/status.js +0 -53
  133. package/src/commands/project/update.js +31 -239
  134. package/src/commands/project/worktree.js +108 -882
  135. package/src/commands/scaffold/scaffold.js +0 -25
  136. package/src/commands/state/advance.js +14 -85
  137. package/src/commands/state/approve.js +14 -96
  138. package/src/commands/state/archive.js +12 -64
  139. package/src/commands/state/auto-approval.js +2 -5
  140. package/src/commands/state/create.js +22 -109
  141. package/src/commands/state/delete.js +11 -63
  142. package/src/commands/state/finish.js +98 -475
  143. package/src/commands/state/gate-check.js +22 -132
  144. package/src/commands/state/gate-decision.js +3 -39
  145. package/src/commands/state/handoff.js +3 -30
  146. package/src/commands/state/list.js +14 -84
  147. package/src/commands/state/report-check.js +8 -58
  148. package/src/commands/state/retype.js +4 -26
  149. package/src/commands/validation/index.js +0 -3
  150. package/src/commands/validation/mutate.js +2 -5
  151. package/src/commands/validation/validate-feature.js +5 -94
  152. package/src/commands/validation/verify.js +15 -98
  153. package/src/core/paths/output-schema.js +2 -101
  154. package/src/core/state/feature-identity.js +1 -25
  155. package/src/core/state/gate-order.js +0 -27
  156. package/src/core/state/lifecycle-audit.js +8 -100
  157. package/src/core/state/state-manager.js +22 -304
  158. package/src/core/templates/template-registry.js +2 -97
  159. package/src/core/templates/template-renderer.js +13 -93
  160. package/src/core/templates/template-seed.js +0 -1
  161. package/src/core/work-types.js +1 -36
  162. package/src/dashboard/app/App.jsx +1 -8
  163. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  164. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  165. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  166. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  167. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  168. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  169. package/src/dashboard/app/components/Markdown.jsx +0 -9
  170. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  171. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  172. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  173. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  174. package/src/dashboard/app/hooks/usePref.js +1 -5
  175. package/src/dashboard/app/lib/format.js +0 -2
  176. package/src/dashboard/app/lib/phases.js +1 -5
  177. package/src/dashboard/app/lib/project.js +0 -12
  178. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  179. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  180. package/src/dashboard/app/pages/Features.jsx +1 -8
  181. package/src/dashboard/app/pages/Guide.jsx +0 -1
  182. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  183. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  184. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  185. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  186. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  187. package/src/dashboard/app/pages/Standards.jsx +2 -17
  188. package/src/dashboard/scanner/activity-scanner.js +10 -69
  189. package/src/dashboard/scanner/agents-scanner.js +1 -23
  190. package/src/dashboard/scanner/docs-scanner.js +0 -6
  191. package/src/dashboard/scanner/feature-status.js +2 -5
  192. package/src/dashboard/scanner/features-scanner.js +10 -66
  193. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  194. package/src/dashboard/scanner/index.js +0 -4
  195. package/src/dashboard/scanner/portfolio.js +5 -29
  196. package/src/dashboard/scanner/project-api.js +8 -54
  197. package/src/dashboard/scanner/rules-scanner.js +1 -2
  198. package/src/dashboard/scanner/skills-scanner.js +2 -12
  199. package/src/dashboard/scanner/standards-scanner.js +2 -6
  200. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  201. package/src/lib/ai/ai-checks.js +7 -52
  202. package/src/lib/ai/ai-project-scan.js +14 -108
  203. package/src/lib/ai/code-audit.js +7 -63
  204. package/src/lib/ai/pin-drift.js +8 -51
  205. package/src/lib/ai/registry-audit.js +11 -90
  206. package/src/lib/ci/ci-verify.js +4 -19
  207. package/src/lib/ci/github-ci.js +9 -37
  208. package/src/lib/ci/workflow-scaffold.js +2 -10
  209. package/src/lib/detectors/claude-config-detector.js +13 -74
  210. package/src/lib/detectors/index.js +4 -50
  211. package/src/lib/e2e/compose-env.js +5 -66
  212. package/src/lib/e2e/stack-teardown.js +6 -40
  213. package/src/lib/generators/architecture-scanner.js +18 -156
  214. package/src/lib/generators/handoff-generator.js +9 -53
  215. package/src/lib/generators/scaffold-generator.js +5 -101
  216. package/src/lib/generators/settings-generator.js +4 -37
  217. package/src/lib/git/branch-classifier.js +11 -84
  218. package/src/lib/git/default-branch.js +14 -169
  219. package/src/lib/git/evidence-staleness.js +1 -0
  220. package/src/lib/git/gh.js +2 -17
  221. package/src/lib/git/pending-prs.js +6 -37
  222. package/src/lib/git/pr-files.js +3 -28
  223. package/src/lib/git/pr-index.js +3 -34
  224. package/src/lib/git/tree-fingerprint.js +3 -15
  225. package/src/lib/git/tree-state.js +6 -33
  226. package/src/lib/graph/god-nodes.js +2 -22
  227. package/src/lib/graph/graph-health.js +3 -24
  228. package/src/lib/graph/graph-loader.js +5 -56
  229. package/src/lib/graph/graphify-runtime.js +7 -37
  230. package/src/lib/graph/graphifyignore.js +8 -74
  231. package/src/lib/graph/sanitize.js +16 -136
  232. package/src/lib/heavy/cleanup.js +1 -1
  233. package/src/lib/installers/effects.js +8 -93
  234. package/src/lib/installers/mcp-installer.js +9 -169
  235. package/src/lib/installers/memory-installer.js +4 -27
  236. package/src/lib/installers/templates-reconcile.js +5 -60
  237. package/src/lib/mutation/dotnet-introspection.js +20 -106
  238. package/src/lib/mutation/subset.js +1 -4
  239. package/src/lib/plan/artifact-vocabulary.js +5 -25
  240. package/src/lib/plan/checks.js +5 -31
  241. package/src/lib/plan/plan-parser.js +1 -6
  242. package/src/lib/projects/registry.js +7 -44
  243. package/src/lib/review-reports.js +5 -23
  244. package/src/lib/risk-detector.js +39 -173
  245. package/src/lib/specs/adr-index.js +6 -34
  246. package/src/lib/specs/frontmatter.js +2 -15
  247. package/src/lib/specs/gfm-table.js +1 -7
  248. package/src/lib/specs/revocations.js +1 -14
  249. package/src/lib/specs/screen-checklist.js +47 -124
  250. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  251. package/src/lib/stack/stack-profile.js +1 -28
  252. package/src/lib/stack/stack-scan.js +1 -8
  253. package/src/lib/stack-filter.js +4 -27
  254. package/src/lib/standards/registry.js +1 -7
  255. package/src/lib/standards/sections.js +2 -13
  256. package/src/lib/standards/verified-against.js +19 -191
  257. package/src/lib/tasks/eval-node.js +21 -169
  258. package/src/lib/tasks/linux-runner.js +18 -142
  259. package/src/lib/tasks/test-nodes.js +43 -240
  260. package/src/lib/tasks/test-runner.js +43 -341
  261. package/src/lib/telemetry/metrics.js +7 -36
  262. package/src/lib/telemetry/telemetry-reader.js +13 -125
  263. package/src/lib/telemetry/verify-event.js +2 -30
  264. package/src/lib/validators/code-budget/code-budget-validator.js +3 -43
  265. package/src/lib/validators/code-budget/index.js +0 -7
  266. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  267. package/src/lib/validators/contracts/index.js +0 -6
  268. package/src/lib/validators/nextjs/index.js +0 -5
  269. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  270. package/src/lib/validators/packages/index.js +0 -6
  271. package/src/lib/validators/packages/package-validator.js +6 -56
  272. package/src/lib/validators/reuse/index.js +0 -8
  273. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  274. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  275. package/src/lib/validators/traceability-check.js +5 -63
  276. package/src/lib/validators/validation-runner.js +20 -125
  277. package/src/lib/validators/verify-runner.js +52 -358
  278. package/src/lib/work-type-detector.js +5 -67
  279. package/src/lib/worktree/docker-audit.js +17 -136
  280. package/src/lib/worktree/env-allocator.js +10 -98
  281. package/src/lib/worktree/feature-truth.js +12 -115
  282. package/src/lib/worktree/finish-backup.js +32 -242
  283. package/src/lib/worktree/fleet-scanner.js +13 -95
  284. package/src/lib/worktree/ignored-triage.js +15 -91
  285. package/src/lib/worktree/vscode-settings.js +2 -3
  286. package/src/lib/worktree/worktree-audit.js +21 -213
  287. package/src/lib/worktree/worktree-include.js +3 -5
  288. package/src/scripts/global-install.js +3 -9
  289. package/src/scripts/install-plugin.js +1 -13
  290. package/src/scripts/setup-infra.js +17 -147
  291. package/src/utils/agents-installer.js +28 -275
  292. package/src/utils/banner.js +4 -25
  293. package/src/utils/claude-md-injector.js +1 -37
  294. package/src/utils/claude-settings-manager.js +1 -52
  295. package/src/utils/file-copier.js +11 -95
  296. package/src/utils/hooks-installer.js +21 -298
  297. package/src/utils/skills-installer.js +4 -101
  298. package/src/utils/version-checker.js +4 -83
  299. package/framework/commands/morph-proposal.md +0 -9
  300. package/src/commands/utils/index.js +0 -3
  301. package/src/lib/detectors/design-system-detector.js +0 -188
  302. package/src/lib/generators/design-system-generator.js +0 -508
  303. package/src/lib/specs/old-format.js +0 -68
  304. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,72 +1,24 @@
1
- /**
2
- * Linux Runner — run the declared test nodes inside a Linux container.
3
- *
4
- * WHY THIS MODULE EXISTS
5
- *
6
- * Defect F35: 3.057 tests green on Windows, ten failures on Linux. The ten
7
- * were `Link=` casing and `Uri.TryCreate` — defects of CASE, invisible on a
8
- * case-insensitive filesystem. The CI has had an ubuntu leg since 8.24.2 and
9
- * F35 still reached the field, because the CI answers minutes-to-hours after
10
- * the loop has moved on. `--linux` is the desk-side complement: the same
11
- * suites, on Linux, inside the loop.
12
- *
13
- * WHY THE TREE IS COPIED AND NOT MOUNTED (D5) — the load-bearing decision
14
- *
15
- * Docker Desktop's bind mount on Windows crosses a 9p / gRPC-FUSE translation
16
- * layer that is **case-insensitive**. Running the suite on top of that mount
17
- * gives back exactly the green Windows already gave: the flag would exist and
18
- * prove nothing about the one class of defect it was built for. So the host
19
- * tree is mounted READ-ONLY at `/src` purely as the source of a copy, the copy
20
- * lands on the container's own filesystem (the overlayfs) at `/work`, and every
21
- * node runs from `/work/{cwd}`. Nothing ever executes out of `/src`.
22
- *
23
- * The copy is `tar | tar` rather than a literal `cp -r` because `cp` has no
24
- * portable exclusion: `node_modules`, `bin`, `obj`, `.git` and the worktree
25
- * container directories must not travel (a worktree is ANOTHER feature's
26
- * checkout — copying it doubles the clock and reports on code outside the tree
27
- * under judgement, the same reason `collectDotnetTestProjects` excludes them).
28
- * The effect is the one the issue asked for: the executed tree lives in the
29
- * container.
30
- *
31
- * WHY `exec` IS INJECTED
32
- *
33
- * The CI has no guaranteed Docker daemon and no .NET at all. Every test of
34
- * this module drives a recorded `exec` and asserts the command line that WOULD
35
- * have run — a verifier whose own tests need a container is a verifier that
36
- * stops being tested.
37
- *
38
- * @module linux-runner
39
- */
40
-
1
+ // The Docker Desktop bind mount is case-insensitive (9p/gRPC-FUSE), so the tree is
2
+ // mounted read-only at /src and COPIED to the container's own filesystem at /work:
3
+ // running on the mount would hide exactly the casing defects this runner exists for.
41
4
  import { createHash } from 'crypto';
42
5
  import { resolve } from 'path';
43
6
 
44
7
  import { defaultExec, isDockerAvailable } from '../../../framework/hooks/shared/e2e-check.js';
45
8
  import { WORKTREES_DIR, LEGACY_WORKTREES_DIR } from '../worktree/dirs.js';
46
9
 
47
- /** The SDK image `--linux` runs in by default (spec §5.1); overridable per run and per node. */
48
10
  export const LINUX_DEFAULT_IMAGE = 'mcr.microsoft.com/dotnet/sdk:10.0';
49
11
 
50
- /** Where the host tree is mounted (read-only) and where the copy is executed from. */
51
12
  const SRC_MOUNT = '/src';
52
13
  const WORK_DIR = '/work';
53
14
 
54
- /**
55
- * Markers that fence one node's output inside the single container's stdout.
56
- * Deliberately noisy: a delimiter that could occur in real test output would
57
- * split a run at the wrong line and attribute failures to the wrong suite.
58
- */
15
+ // Deliberately noisy: a delimiter that could occur in real test output would
16
+ // attribute failures to the wrong suite.
59
17
  export const NODE_START = '@@MORPH-NODE-START@@';
60
18
  export const NODE_EXIT = '@@MORPH-NODE-EXIT@@';
61
19
 
62
- /**
63
- * What never travels into the container.
64
- *
65
- * The two worktree container names are IMPORTED from `dirs.js`, never retyped:
66
- * duplicating that string is the documented way the C1 bug comes back. They are
67
- * anchored at the root (`./name`) because they are root-level containers; the
68
- * build artifacts are unanchored because they nest anywhere in a tree.
69
- */
20
+ // Worktree dirs are anchored at the root (`./name`); build artifacts nest anywhere.
21
+ // A worktree is another feature's checkout and must never be copied.
70
22
  export const COPY_EXCLUDES = [
71
23
  'node_modules',
72
24
  'bin',
@@ -76,21 +28,12 @@ export const COPY_EXCLUDES = [
76
28
  `./${LEGACY_WORKTREES_DIR}`,
77
29
  ];
78
30
 
79
- /** Single-quote a POSIX shell word — the container's shell is always sh. */
80
31
  function shq(value) {
81
32
  return `'${String(value).replace(/'/g, `'\\''`)}'`;
82
33
  }
83
34
 
84
- /**
85
- * Why `--linux` cannot run, or null when it can.
86
- *
87
- * A missing daemon is a USAGE ERROR (exit 2), never a skipped node. A skip
88
- * would mean "we could not check on Linux" reading as green — which is F35
89
- * itself, restated as a feature.
90
- *
91
- * @param {{ projectPath: string, skipTests: boolean, exec?: Function }} params
92
- * @returns {string|null}
93
- */
35
+ // A missing daemon is a usage error, never a skipped node: a skip would read
36
+ // "could not check on Linux" as green.
94
37
  export function checkLinuxPreconditions({ projectPath, skipTests, exec = defaultExec }) {
95
38
  if (skipTests) {
96
39
  return '--linux com --skip-tests se anulam: a flag existe para RODAR a suíte em Linux';
@@ -102,14 +45,8 @@ export function checkLinuxPreconditions({ projectPath, skipTests, exec = default
102
45
  return null;
103
46
  }
104
47
 
105
- /**
106
- * Which nodes can actually run inside the container, and why the others cannot.
107
- *
108
- * An `e2e: true` node needs the application stack up; bringing docker up INSIDE
109
- * a container is out of scope, so it is refused by name rather than silently
110
- * dropped — a suite that disappears from the report is the defect this whole
111
- * feature exists to close.
112
- */
48
+ // e2e nodes are refused by name, never dropped: a suite that disappears from the
49
+ // report reads as green.
113
50
  function partitionNodes(nodes) {
114
51
  const runnable = [];
115
52
  const refused = [];
@@ -130,63 +67,31 @@ function partitionNodes(nodes) {
130
67
  return { runnable, refused };
131
68
  }
132
69
 
133
- /**
134
- * The shell script the container executes: copy first, then every node.
135
- *
136
- * Order is the contract — the copy precedes the first command, and every
137
- * command is `cd`-ed into `/work/{cwd}`. `set -e` is deliberately NOT used
138
- * around the nodes: one red suite must not cancel the ones after it (the same
139
- * rule the local loop follows for a timeout).
140
- *
141
- * @param {{ nodes: Array<import('./test-nodes.js').TestNode> }} params
142
- * @returns {string}
143
- */
70
+ // No `set -e` around the nodes: one red suite must not cancel the ones after it.
144
71
  export function buildContainerScript({ nodes }) {
145
72
  const excludes = COPY_EXCLUDES.map((e) => `--exclude=${e}`).join(' ');
146
73
  const lines = [
147
74
  'set -u',
148
75
  `mkdir -p ${WORK_DIR}`,
149
- // The copy that makes this flag mean something: after it, the executed
150
- // files live on the container's own filesystem, which is case-SENSITIVE.
151
76
  `tar -C ${SRC_MOUNT} ${excludes} -cf - . | tar -C ${WORK_DIR} -xf -`,
152
77
  ];
153
78
 
154
79
  for (const node of nodes) {
155
80
  const dir = `${WORK_DIR}/${node.cwd || '.'}`;
156
- // The clock is read into a variable BEFORE the marker is echoed, and the
157
- // exit code into another one: a `$(date)` sharing an echo with `$?` is one
158
- // shell quirk away from reporting the timestamp's exit code as the suite's.
159
- // An image without `date` degrades to an UNKNOWN duration, never a zero.
81
+ // `$?` is captured into its own variable: sharing an echo with `$(date)` can
82
+ // report the timestamp's exit code as the suite's.
160
83
  lines.push(`__ms=$(date +%s 2>/dev/null); echo "${NODE_START} ${node.id} $__ms"`);
161
84
  lines.push(`cd ${shq(dir)} && ${node.command}`);
162
- // `$?` of the compound above: a cd that fails is a failure too, and one
163
- // that reported success would run the suite from the wrong directory.
164
85
  lines.push(`__code=$?; __me=$(date +%s 2>/dev/null); echo "${NODE_EXIT} ${node.id} $__code $__me"`);
165
86
  }
166
87
 
167
88
  return lines.join('\n');
168
89
  }
169
90
 
170
- /**
171
- * The exact `docker` argv. Split out from the execution so a test can assert
172
- * the command line without a daemon — this is the canary that keeps the
173
- * read-only `/src` and the absence of a writable `/work` mount honest.
174
- *
175
- * @param {{ projectPath: string, image: string, script: string }} params
176
- * @returns {string[]}
177
- */
178
91
  export function buildLinuxArgv({ projectPath, image, script, runId = null }) {
179
92
  return [
180
93
  'run', '--rm',
181
94
  ...(runId ? ['--label', `${LABEL_RUN}=${runId}`, '--label', `${LABEL_ROOT}=${rootHash(projectPath)}`] : []),
182
- // Read-only, and mounted at /src: the SOURCE of the copy, never the tree
183
- // the tests run from. See the module docs for why that distinction is the
184
- // entire point of the flag.
185
- //
186
- // Forward slashes even on Windows: `C:\a\b` and `C:/a/b` are both accepted
187
- // by Docker Desktop, but the backslash form is the one that breaks first
188
- // when a path is re-parsed downstream, and the mount spec is already
189
- // colon-delimited.
190
95
  '-v', `${resolve(projectPath).replace(/\\/g, '/')}:${SRC_MOUNT}:ro`,
191
96
  '-w', WORK_DIR,
192
97
  image,
@@ -194,13 +99,7 @@ export function buildLinuxArgv({ projectPath, image, script, runId = null }) {
194
99
  ];
195
100
  }
196
101
 
197
- /**
198
- * Split the container's stdout back into one result per node.
199
- *
200
- * A node whose exit marker never arrived did not pass: the container died
201
- * mid-run (image pull, OOM, killed clock). Reporting it as anything but a
202
- * failure would be a green verdict over a suite nobody observed.
203
- */
102
+ // A node whose exit marker never arrived did not pass: the container died mid-run.
204
103
  function splitByMarkers(output, nodes) {
205
104
  const text = String(output || '');
206
105
  const escape = (v) => String(v).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -229,8 +128,7 @@ function splitByMarkers(output, nodes) {
229
128
  reason: 'o container terminou sem reportar o código de saída deste nó',
230
129
  };
231
130
  }
232
- // **null is UNKNOWN, never 0** — an image without `date` did not run the
233
- // suite in zero milliseconds.
131
+ // null is UNKNOWN, never 0: an image without `date` did not run in zero ms.
234
132
  const durationMs = startMatch[1] && exitMatch[2]
235
133
  ? (Number(exitMatch[2]) - Number(startMatch[1])) * 1000
236
134
  : null;
@@ -239,29 +137,11 @@ function splitByMarkers(output, nodes) {
239
137
  }
240
138
 
241
139
  /**
242
- * Run every runnable node inside ONE container and return the raw per-node
243
- * results — exit code and output, nothing judged.
244
- *
245
- * Judgement (count comparison, baseline drop, feedback lines) stays in
246
- * verify-runner beside the local path, so a container run and a host run are
247
- * decided by the SAME code. A second copy of that rule is how two environments
248
- * start disagreeing about the same suite.
249
- *
250
- * @param {Object} params
251
- * @param {string} params.projectPath - Project root (the copy source)
252
- * @param {Array<import('./test-nodes.js').TestNode>} params.nodes - Nodes to run
253
- * @param {string} [params.image] - Image override (`--linux-image`)
254
- * @param {number} [params.timeout] - Ceiling for the whole container, in ms
255
- * @param {Function} [params.exec] - Injected executor (defaults to e2e-check's defaultExec)
256
- * @returns {{ results: Array<{id: string, exitCode: number|null, output: string, reason: string|null, skipped?: boolean}>,
257
- * environment: {kind: string, image: string}, argv: string[], script: string, containerCode: number|null }}
140
+ * Runs every runnable node in ONE container and returns raw per-node results,
141
+ * unjudged: verify-runner judges container and host runs with the same code.
258
142
  */
259
143
  export function runLinuxSuite({ projectPath, nodes, image, timeout, exec = defaultExec }) {
260
144
  const { runnable, refused } = partitionNodes(nodes);
261
- // Precedence: explicit override > a node that declares its own image > the
262
- // default. A per-node image only decides the single container everyone shares
263
- // when it is the only one asked for — two images would mean two copies of the
264
- // tree and two clocks, which is not what this flag is for.
265
145
  const declared = runnable.map((n) => n.linuxImage).filter(Boolean);
266
146
  const resolvedImage = image || declared[0] || LINUX_DEFAULT_IMAGE;
267
147
  const environment = { kind: 'linux-container', image: resolvedImage };
@@ -286,9 +166,6 @@ export function runLinuxSuite({ projectPath, nodes, image, timeout, exec = defau
286
166
  : r
287
167
  );
288
168
 
289
- // A container that never produced a single marker failed as a whole (a pull
290
- // failure, say). The reason has to name the container, or the reader goes
291
- // hunting through a test log for a suite that never started.
292
169
  const nothingRan = results.every((r) => r.exitCode === null);
293
170
  const enriched = results.map((r) =>
294
171
  r.exitCode === null && nothingRan
@@ -318,7 +195,6 @@ export function reapLabeledContainers({ runId, exec, cwd }) {
318
195
  } catch { /* limpeza best-effort: nunca muda o veredito da suíte */ }
319
196
  }
320
197
 
321
- /** Last few lines of the container output — enough to name a pull failure. */
322
198
  function tail(output, lines = 5) {
323
199
  return String(output || '').split('\n').slice(-lines).join('\n').trim();
324
200
  }