@ornexus/neocortex 4.60.73 → 4.60.75

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 (269) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1216 -1210
  3. package/install.ps1 +1 -1
  4. package/install.sh +1 -1
  5. package/package.json +2 -2
  6. package/packages/client/dist/adapters/adapter-registry.js +1 -108
  7. package/packages/client/dist/adapters/antigravity-adapter.js +2 -77
  8. package/packages/client/dist/adapters/claude-code-adapter.js +3 -79
  9. package/packages/client/dist/adapters/codex-adapter.js +2 -80
  10. package/packages/client/dist/adapters/cursor-adapter.js +4 -115
  11. package/packages/client/dist/adapters/gemini-adapter.js +2 -71
  12. package/packages/client/dist/adapters/grok-adapter.js +2 -42
  13. package/packages/client/dist/adapters/index.js +1 -22
  14. package/packages/client/dist/adapters/platform-detector.js +1 -212
  15. package/packages/client/dist/adapters/target-adapter.js +0 -12
  16. package/packages/client/dist/adapters/vscode-adapter.js +2 -72
  17. package/packages/client/dist/agent/refresh-stubs.js +2 -417
  18. package/packages/client/dist/agent/update-agent-yaml.js +1 -102
  19. package/packages/client/dist/agent/update-description.js +1 -251
  20. package/packages/client/dist/cache/crypto-utils.js +1 -78
  21. package/packages/client/dist/cache/encrypted-cache.d.ts +27 -0
  22. package/packages/client/dist/cache/encrypted-cache.js +1 -108
  23. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
  24. package/packages/client/dist/cache/index.js +1 -13
  25. package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
  26. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
  27. package/packages/client/dist/checkpoint/index.js +1 -11
  28. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
  29. package/packages/client/dist/cli.js +5 -358
  30. package/packages/client/dist/commands/activate.js +8 -394
  31. package/packages/client/dist/commands/architecture-answers.js +7 -274
  32. package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
  33. package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
  34. package/packages/client/dist/commands/architecture-root-binding.js +1 -51
  35. package/packages/client/dist/commands/cache-status.js +2 -112
  36. package/packages/client/dist/commands/config.js +1 -88
  37. package/packages/client/dist/commands/doctor.js +2 -635
  38. package/packages/client/dist/commands/invoke.d.ts +38 -1
  39. package/packages/client/dist/commands/invoke.js +143 -7200
  40. package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
  41. package/packages/client/dist/commands/legacy-story-branch.js +1 -96
  42. package/packages/client/dist/commands/legacy-story-pr.js +1 -276
  43. package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
  44. package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
  45. package/packages/client/dist/commands/mutation-qa-git.js +1 -41
  46. package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
  47. package/packages/client/dist/commands/mutation-qa-run.js +3 -219
  48. package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
  49. package/packages/client/dist/commands/mutation-qa.js +1 -220
  50. package/packages/client/dist/commands/native-agent.js +2 -136
  51. package/packages/client/dist/commands/planning-diagnosis.js +1 -43
  52. package/packages/client/dist/commands/planning-generation.js +1 -53
  53. package/packages/client/dist/commands/planning-memory.js +1 -40
  54. package/packages/client/dist/commands/planning-research.js +1 -42
  55. package/packages/client/dist/commands/private-session.js +1 -75
  56. package/packages/client/dist/commands/property-qa.js +1 -139
  57. package/packages/client/dist/commands/qa-control.js +1 -16
  58. package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
  59. package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
  60. package/packages/client/dist/commands/qa-scan-capture.js +5 -663
  61. package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
  62. package/packages/client/dist/commands/qa-scan-review.js +5 -826
  63. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
  64. package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
  65. package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
  66. package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
  67. package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
  68. package/packages/client/dist/commands/refactor-characterization.js +21 -671
  69. package/packages/client/dist/commands/refactor-commit.js +13 -399
  70. package/packages/client/dist/commands/refactor-control.js +3 -142
  71. package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
  72. package/packages/client/dist/commands/refactor-review.js +5 -283
  73. package/packages/client/dist/commands/refactor-root.js +7 -588
  74. package/packages/client/dist/commands/refresh-memory.js +3 -156
  75. package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
  76. package/packages/client/dist/commands/story-checkpoint.js +1 -171
  77. package/packages/client/dist/config/host-auth-broker.d.ts +31 -0
  78. package/packages/client/dist/config/host-auth-broker.js +1 -0
  79. package/packages/client/dist/config/host-auth-reference.d.ts +32 -0
  80. package/packages/client/dist/config/host-auth-reference.js +1 -0
  81. package/packages/client/dist/config/profile-directory.js +1 -13
  82. package/packages/client/dist/config/resolver-selection.js +1 -278
  83. package/packages/client/dist/config/secure-config.js +12 -281
  84. package/packages/client/dist/config.js +2 -223
  85. package/packages/client/dist/constants.js +1 -25
  86. package/packages/client/dist/context/context-collector.js +2 -222
  87. package/packages/client/dist/context/context-sanitizer.js +1 -145
  88. package/packages/client/dist/continuity/autonomous-governance.js +1 -285
  89. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
  90. package/packages/client/dist/continuity/invoke-hooks.js +1 -129
  91. package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
  92. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
  93. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
  94. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
  95. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
  96. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
  97. package/packages/client/dist/continuity/native-agent-store.js +1 -69
  98. package/packages/client/dist/continuity/native-agent-types.d.ts +11 -0
  99. package/packages/client/dist/continuity/native-agent-types.js +1 -90
  100. package/packages/client/dist/continuity/outbox.js +1 -117
  101. package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
  102. package/packages/client/dist/continuity/sqlite-store.js +81 -2612
  103. package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
  104. package/packages/client/dist/errors/error-messages.d.ts +3 -0
  105. package/packages/client/dist/errors/error-messages.js +2 -228
  106. package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
  107. package/packages/client/dist/evidence/collector.js +1 -347
  108. package/packages/client/dist/evidence/index.js +1 -1
  109. package/packages/client/dist/evidence/types.js +0 -2
  110. package/packages/client/dist/fs/atomic-state-replace.js +1 -152
  111. package/packages/client/dist/generated/command-registry.generated.js +1 -4076
  112. package/packages/client/dist/generated/public-projections.generated.js +1 -149
  113. package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
  114. package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +11 -1
  115. package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
  116. package/packages/client/dist/git/epic-operational-state.js +2 -672
  117. package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
  118. package/packages/client/dist/git/git-lifecycle-store.d.ts +23 -0
  119. package/packages/client/dist/git/git-lifecycle-store.js +10 -1981
  120. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
  121. package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
  122. package/packages/client/dist/git/legacy-story-pr.js +70 -3538
  123. package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
  124. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
  125. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
  126. package/packages/client/dist/i18n/first-run.js +2 -60
  127. package/packages/client/dist/index.d.ts +2 -0
  128. package/packages/client/dist/index.js +1 -47
  129. package/packages/client/dist/invocation/invoke-input.js +1 -24
  130. package/packages/client/dist/license/host-auth-recovery.d.ts +158 -0
  131. package/packages/client/dist/license/host-auth-recovery.js +1 -0
  132. package/packages/client/dist/license/index.js +1 -5
  133. package/packages/client/dist/license/license-client.d.ts +10 -1
  134. package/packages/client/dist/license/license-client.js +1 -304
  135. package/packages/client/dist/machine/fingerprint.js +2 -163
  136. package/packages/client/dist/machine/index.js +1 -5
  137. package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
  138. package/packages/client/dist/memory/project-memory-writer.js +40 -1007
  139. package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
  140. package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -1060
  141. package/packages/client/dist/migrations/project-identity-capability.js +1 -50
  142. package/packages/client/dist/migrations/project-identity.js +7 -493
  143. package/packages/client/dist/migrations/state-json.js +4 -393
  144. package/packages/client/dist/policy/architecture-policy.js +2 -184
  145. package/packages/client/dist/policy/index.js +1 -7
  146. package/packages/client/dist/policy/shared-policy-types.js +0 -38
  147. package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
  148. package/packages/client/dist/private-execution/clean-baseline.js +2 -279
  149. package/packages/client/dist/private-execution/codex-executor.js +4 -574
  150. package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
  151. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
  152. package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
  153. package/packages/client/dist/private-execution/delivery.js +13 -410
  154. package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
  155. package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
  156. package/packages/client/dist/private-execution/implementation-validation.js +2 -106
  157. package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
  158. package/packages/client/dist/private-execution/isolated-session.js +5 -725
  159. package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
  160. package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
  161. package/packages/client/dist/private-execution/pact-test-source.js +1 -258
  162. package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
  163. package/packages/client/dist/private-execution/pact-write-set.js +1 -172
  164. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
  165. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
  166. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
  167. package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
  168. package/packages/client/dist/private-execution/private-report-store.js +1 -42
  169. package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
  170. package/packages/client/dist/private-execution/property-test-choices.js +3 -147
  171. package/packages/client/dist/private-execution/property-test-plan.js +7 -271
  172. package/packages/client/dist/private-execution/property-test-review.js +4 -258
  173. package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
  174. package/packages/client/dist/private-execution/property-test-source.js +1 -139
  175. package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
  176. package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
  177. package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
  178. package/packages/client/dist/private-execution/root-memory-request.js +1 -29
  179. package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
  180. package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
  181. package/packages/client/dist/private-execution/session-lease.js +1 -63
  182. package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
  183. package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
  184. package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
  185. package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
  186. package/packages/client/dist/private-execution/ux-write-set.js +2 -191
  187. package/packages/client/dist/registry/registry-reconciliation.js +1 -234
  188. package/packages/client/dist/resilience/circuit-breaker.js +1 -170
  189. package/packages/client/dist/resilience/degradation-manager.js +1 -164
  190. package/packages/client/dist/resilience/freshness-indicator.js +1 -100
  191. package/packages/client/dist/resilience/index.js +1 -8
  192. package/packages/client/dist/resilience/recovery-detector.js +1 -74
  193. package/packages/client/dist/resolvers/asset-resolver.js +0 -13
  194. package/packages/client/dist/resolvers/local-resolver.js +8 -255
  195. package/packages/client/dist/resolvers/remote-resolver.js +1 -233
  196. package/packages/client/dist/runner/binary.js +1 -404
  197. package/packages/client/dist/runner/cli.js +17 -1571
  198. package/packages/client/dist/runner/epic-background-dispatcher.d.ts +61 -0
  199. package/packages/client/dist/runner/epic-background-dispatcher.js +1 -0
  200. package/packages/client/dist/runner/headless/adapters.js +1 -49
  201. package/packages/client/dist/runner/headless/background-root.d.ts +65 -0
  202. package/packages/client/dist/runner/headless/background-root.js +1 -0
  203. package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
  204. package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
  205. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
  206. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
  207. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
  208. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
  209. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -1236
  210. package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +27 -2
  211. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
  212. package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
  213. package/packages/client/dist/runner/headless/managed-environment-cleanup.d.ts +95 -0
  214. package/packages/client/dist/runner/headless/managed-environment-cleanup.js +1 -0
  215. package/packages/client/dist/runner/headless/managed-profile.js +1 -132
  216. package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
  217. package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
  218. package/packages/client/dist/runner/headless/resolver.js +1 -231
  219. package/packages/client/dist/runner/headless/run-yolo.js +1 -885
  220. package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
  221. package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
  222. package/packages/client/dist/runner/headless/types.js +2 -574
  223. package/packages/client/dist/runner/local-console.js +13 -396
  224. package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
  225. package/packages/client/dist/runner/project-identity.js +1 -106
  226. package/packages/client/dist/runner/scheduler.js +7 -995
  227. package/packages/client/dist/runner-cli.js +2 -18
  228. package/packages/client/dist/state/invocation-shape.js +1 -13
  229. package/packages/client/dist/state/project-state-snapshot.d.ts +7 -0
  230. package/packages/client/dist/state/project-state-snapshot.js +1 -1731
  231. package/packages/client/dist/state/state-json-repair.js +3 -785
  232. package/packages/client/dist/state/structural-dag-projection.js +1 -118
  233. package/packages/client/dist/telemetry/index.js +1 -5
  234. package/packages/client/dist/telemetry/offline-queue.js +1 -131
  235. package/packages/client/dist/tier/index.js +1 -5
  236. package/packages/client/dist/tier/tier-aware-client.js +1 -405
  237. package/packages/client/dist/types/index.js +1 -38
  238. package/packages/client/dist/version.js +1 -27
  239. package/packages/client/dist/yolo/change-evidence-collector.d.ts +21 -1
  240. package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
  241. package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
  242. package/packages/client/dist/yolo/spec-envelope-collector.js +5 -184
  243. package/packages/client/dist/yoloop/discovery-hook.js +2 -300
  244. package/packages/client/dist/yoloop/index.js +1 -8
  245. package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
  246. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
  247. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
  248. package/packages/client/dist/yoloop/target-normalization.js +1 -180
  249. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1920
  250. package/packages/shared/dist/invocation/explicit-intent-recovery.d.ts +83 -0
  251. package/packages/shared/dist/invocation/explicit-intent-recovery.js +187 -0
  252. package/packages/shared/dist/invocation/index.d.ts +7 -0
  253. package/packages/shared/dist/invocation/index.js +22 -0
  254. package/packages/shared/dist/yolo/change-evidence.js +1 -1
  255. package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
  256. package/targets-stubs/antigravity/gemini.md +1 -1
  257. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  258. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  259. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  260. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  261. package/targets-stubs/claude-code/neocortex.md +2 -2
  262. package/targets-stubs/codex/AGENTS.md +24 -21
  263. package/targets-stubs/codex/README.md +6 -1
  264. package/targets-stubs/codex/neocortex.toml +7 -2
  265. package/targets-stubs/cursor/agent.md +2 -2
  266. package/targets-stubs/gemini-cli/agent.md +2 -2
  267. package/targets-stubs/opencode/neocortex-root.md +16 -1
  268. package/targets-stubs/opencode/neocortex.md +6 -0
  269. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -1,671 +1,21 @@
1
- /** Root-owned, no-network observation for admitted Node JS/TS, Python or C++ tests. */
2
- import { spawnSync } from 'node:child_process';
3
- import { createHash, randomBytes } from 'node:crypto';
4
- import { closeSync, constants, existsSync, fsyncSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
5
- import { tmpdir } from 'node:os';
6
- import { basename, dirname, join, resolve } from 'node:path';
7
- import { mutationQaGit } from './mutation-qa-git.js';
8
- import { exactCommittedRefactorFile, privateRefactorDirectory, readPrivateRefactorAdmission, verifyRootRefactorForCharacterization } from './refactor-root.js';
9
- const SHA256 = /^[a-f0-9]{64}$/;
10
- const MAX_RUN_BYTES = 64 * 1024 * 1024;
11
- const MAX_TESTS = 32;
12
- const MAX_DEPENDENCIES = 128;
13
- /** Node's native type-stripping switch first exists in 22.6.0. */
14
- export function supportsNativeTypeStrippingVersion(version) {
15
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
16
- if (!match)
17
- return false;
18
- const major = Number(match[1]);
19
- const minor = Number(match[2]);
20
- return major > 22 || (major === 22 && minor >= 6);
21
- }
22
- /** Distribution capability is separate from semver; some Node builds disable Amaro. */
23
- export function supportsNativeTypeStrippingRuntime() {
24
- return supportsNativeTypeStrippingVersion(process.versions.node)
25
- && process.config.variables.node_use_amaro === true;
26
- }
27
- function digest(bytes) {
28
- return createHash('sha256').update(bytes).digest('hex');
29
- }
30
- function safePath(path) {
31
- return path.length > 0 && path.length <= 512
32
- && /^[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)*$/.test(path)
33
- && path.split('/').every((part) => part !== '.' && part !== '..'
34
- && !['.git', '.neocortex', 'node_modules'].includes(part.toLowerCase()));
35
- }
36
- function ordered(paths) {
37
- return [...paths].sort((a, b) => Buffer.from(a).compare(Buffer.from(b)));
38
- }
39
- function requireExplicitJsModuleType(root, path, dependencies) {
40
- if (!path.endsWith('.js') && !path.endsWith('.ts'))
41
- return;
42
- let directory = dirname(path);
43
- while (true) {
44
- const manifest = directory === '.' ? 'package.json' : `${directory}/package.json`;
45
- if (existsSync(join(root, manifest))) {
46
- if (!dependencies.includes(manifest))
47
- throw new Error('refactor-root-tests-manifest-unbound');
48
- let parsed;
49
- try {
50
- parsed = JSON.parse(physicalBytes(root, manifest).toString('utf8'));
51
- }
52
- catch {
53
- throw new Error('refactor-root-tests-manifest-invalid');
54
- }
55
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)
56
- || !['module', 'commonjs'].includes(parsed.type)
57
- || (path.endsWith('.ts') && parsed.type !== 'module')) {
58
- throw new Error('refactor-root-tests-manifest-invalid');
59
- }
60
- return;
61
- }
62
- if (directory === '.')
63
- break;
64
- directory = dirname(directory);
65
- }
66
- throw new Error('refactor-root-tests-manifest-missing');
67
- }
68
- function physicalBytes(root, path) {
69
- if (!safePath(path))
70
- throw new Error('refactor-root-tests-path-invalid');
71
- const physicalPath = join(root, path);
72
- const stat = lstatSync(physicalPath);
73
- if (realpathSync(physicalPath) !== physicalPath || !stat.isFile() || stat.isSymbolicLink()
74
- || stat.size < 1 || stat.size > 8 * 1024 * 1024) {
75
- throw new Error('refactor-root-tests-source-unverified');
76
- }
77
- const bytes = readFileSync(physicalPath);
78
- if (bytes.length < 1 || bytes.length > 8 * 1024 * 1024 || bytes.includes(0)) {
79
- throw new Error('refactor-root-tests-source-unverified');
80
- }
81
- new TextDecoder('utf-8', { fatal: true }).decode(bytes);
82
- return bytes;
83
- }
84
- function baselinePath(root, sha256) {
85
- if (!SHA256.test(sha256))
86
- throw new Error('refactor-root-baseline-invalid');
87
- return join(privateRefactorDirectory(root), `characterization-baseline-${sha256}.json`);
88
- }
89
- export function readRootRefactorBaseline(root, sha256) {
90
- const stat = lstatSync(baselinePath(root, sha256));
91
- if (!stat.isFile() || stat.isSymbolicLink() || stat.uid !== process.getuid?.()
92
- || (stat.mode & 0o077) !== 0 || stat.size < 100 || stat.size > 64 * 1024) {
93
- throw new Error('refactor-root-baseline-invalid');
94
- }
95
- const raw = readFileSync(baselinePath(root, sha256));
96
- if (raw.length > 64 * 1024)
97
- throw new Error('refactor-root-baseline-invalid');
98
- let parsed;
99
- try {
100
- parsed = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(raw));
101
- }
102
- catch {
103
- throw new Error('refactor-root-baseline-invalid');
104
- }
105
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
106
- throw new Error('refactor-root-baseline-invalid');
107
- }
108
- const value = parsed;
109
- const keys = ['schemaVersion', 'kind', 'admissionSha256', 'sourceHead', 'testFiles',
110
- 'dependencies', 'causalPath', 'candidateSha256', 'outputSha256',
111
- 'causalOutputSha256', 'baselineSha256'];
112
- const filesValid = (files) => Array.isArray(files)
113
- && files.every((file) => file && typeof file === 'object' && !Array.isArray(file)
114
- && Object.keys(file).sort().join('\0') === 'path\0sha256'
115
- && typeof file.path === 'string'
116
- && typeof file.sha256 === 'string'
117
- && safePath(file.path) && SHA256.test(file.sha256));
118
- const boundRunner = value.schemaVersion === 2;
119
- if (Object.keys(value).sort().join('\0') !== (boundRunner
120
- ? [...keys, 'runner', 'toolchainSha256'] : keys).sort().join('\0')
121
- || (value.schemaVersion !== 1 && value.schemaVersion !== 2)
122
- || (boundRunner && (!['vitest', 'cpp'].includes(String(value.runner)) || !SHA256.test(String(value.toolchainSha256))))
123
- || value.kind !== 'root-refactor-characterization-baseline'
124
- || !SHA256.test(String(value.admissionSha256))
125
- || typeof value.sourceHead !== 'string' || !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(value.sourceHead)
126
- || !filesValid(value.testFiles) || !filesValid(value.dependencies)
127
- || value.testFiles.length < 1 || value.testFiles.length > MAX_TESTS
128
- || value.dependencies.length > MAX_DEPENDENCIES
129
- || typeof value.causalPath !== 'string' || !safePath(value.causalPath)
130
- || !SHA256.test(String(value.candidateSha256)) || !SHA256.test(String(value.outputSha256))
131
- || !SHA256.test(String(value.causalOutputSha256))
132
- || value.baselineSha256 !== sha256)
133
- throw new Error('refactor-root-baseline-invalid');
134
- const tests = value.testFiles;
135
- const dependencies = value.dependencies;
136
- if (new Set([...tests, ...dependencies].map((file) => file.path)).size
137
- !== tests.length + dependencies.length
138
- || tests.map((file) => file.path).join('\0') !== ordered(tests.map((file) => file.path)).join('\0')
139
- || dependencies.map((file) => file.path).join('\0')
140
- !== ordered(dependencies.map((file) => file.path)).join('\0')) {
141
- throw new Error('refactor-root-baseline-invalid');
142
- }
143
- const { baselineSha256: _digest, ...body } = value;
144
- if (digest(JSON.stringify(body)) !== sha256
145
- || !raw.equals(Buffer.from(`${JSON.stringify(value)}\n`))) {
146
- throw new Error('refactor-root-baseline-invalid');
147
- }
148
- return value;
149
- }
150
- function persistBaseline(root, body) {
151
- const baselineSha256 = digest(JSON.stringify(body));
152
- const path = baselinePath(root, baselineSha256);
153
- const record = { ...body, baselineSha256 };
154
- const bytes = Buffer.from(`${JSON.stringify(record)}\n`);
155
- if (existsSync(path)) {
156
- readRootRefactorBaseline(root, baselineSha256);
157
- }
158
- else {
159
- const handle = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL
160
- | (constants.O_NOFOLLOW ?? 0), 0o600);
161
- try {
162
- writeFileSync(handle, bytes);
163
- fsyncSync(handle);
164
- }
165
- finally {
166
- closeSync(handle);
167
- }
168
- const directoryHandle = openSync(privateRefactorDirectory(root), constants.O_RDONLY | (constants.O_DIRECTORY ?? 0));
169
- try {
170
- fsyncSync(directoryHandle);
171
- }
172
- finally {
173
- closeSync(directoryHandle);
174
- }
175
- }
176
- readRootRefactorBaseline(root, baselineSha256);
177
- return baselineSha256;
178
- }
179
- function admittedPaths(record) {
180
- return [...record.files.map((file) => file.path),
181
- ...(record.schemaVersion === 2 ? record.newFiles.map((file) => file.path) : [])];
182
- }
183
- function snapshot(root, record, dependencies, pendingDocumentationPaths = []) {
184
- const files = new Map();
185
- let totalBytes = 0;
186
- for (const path of [...admittedPaths(record), ...dependencies]) {
187
- if (pendingDocumentationPaths.includes(path)) {
188
- try {
189
- lstatSync(join(root, path));
190
- }
191
- catch (error) {
192
- if (error.code === 'ENOENT')
193
- continue;
194
- throw error;
195
- }
196
- throw new Error('refactor-root-tests-source-stale');
197
- }
198
- const bytes = physicalBytes(root, path);
199
- totalBytes += bytes.length;
200
- if (totalBytes > MAX_RUN_BYTES)
201
- throw new Error('refactor-root-tests-snapshot-too-large');
202
- files.set(path, bytes);
203
- }
204
- const candidateSha256 = digest(JSON.stringify([
205
- ...[...files].map(([path, bytes]) => [path, digest(bytes)]),
206
- ...pendingDocumentationPaths.map(path => [path, null]),
207
- ]));
208
- return { files, candidateSha256 };
209
- }
210
- function exactCppToolchain() {
211
- try {
212
- const executable = realpathSync('/usr/bin/g++');
213
- const stat = lstatSync(executable);
214
- if (!executable.startsWith('/usr/') || !/^[A-Za-z0-9_./+-]+$/.test(executable)
215
- || !stat.isFile() || stat.uid !== 0 || (stat.mode & 0o022) !== 0
216
- || stat.size < 1 || stat.size > 32 * 1024 * 1024)
217
- throw new Error('invalid compiler');
218
- const result = spawnSync(executable, ['-dumpfullversion', '-dumpversion'], {
219
- env: { PATH: '/usr/bin:/bin' }, encoding: 'utf8', timeout: 10_000, maxBuffer: 4096,
220
- });
221
- const version = result.stdout?.trim();
222
- if (result.status !== 0 || !/^(?:11|12|13|14|15)\.[0-9]+\.[0-9]+$/.test(version)) {
223
- throw new Error('unsupported compiler');
224
- }
225
- return { executable, sha256: digest(JSON.stringify([executable, version,
226
- digest(readFileSync(executable))])) };
227
- }
228
- catch {
229
- throw new Error('refactor-root-tests-toolchain-unverified');
230
- }
231
- }
232
- /** Bind an installed runner to the committed manifest and lock, never run npm or project scripts. */
233
- function exactVitestToolchain(root, tests, dependencies) {
234
- let packagePath = '';
235
- for (const test of tests) {
236
- let directory = dirname(test);
237
- let nearest = '';
238
- while (true) {
239
- const candidate = directory === '.' ? 'package.json' : `${directory}/package.json`;
240
- if (existsSync(join(root, candidate))) {
241
- nearest = candidate;
242
- break;
243
- }
244
- if (directory === '.')
245
- break;
246
- directory = dirname(directory);
247
- }
248
- if (!nearest || packagePath && nearest !== packagePath) {
249
- throw new Error('refactor-root-tests-toolchain-unverified');
250
- }
251
- packagePath = nearest;
252
- }
253
- if (!dependencies.includes(packagePath) || !dependencies.includes('package-lock.json')) {
254
- throw new Error('refactor-root-tests-toolchain-unbound');
255
- }
256
- const manifestBytes = physicalBytes(root, packagePath);
257
- const lockBytes = physicalBytes(root, 'package-lock.json');
258
- let manifest;
259
- let lock;
260
- try {
261
- manifest = JSON.parse(manifestBytes.toString('utf8'));
262
- lock = JSON.parse(lockBytes.toString('utf8'));
263
- }
264
- catch {
265
- throw new Error('refactor-root-tests-toolchain-unverified');
266
- }
267
- const declared = manifest.devDependencies?.vitest
268
- ?? manifest.dependencies?.vitest;
269
- const packages = lock.packages;
270
- const lockWorkspace = packagePath === 'package.json' ? '' : dirname(packagePath);
271
- const lockedPackage = packages?.[lockWorkspace];
272
- const lockedRequest = lockedPackage?.devDependencies?.vitest
273
- ?? lockedPackage?.dependencies?.vitest;
274
- const lockedVersion = packages?.['node_modules/vitest']?.version;
275
- const lockedViteVersion = packages?.['node_modules/vite']?.version;
276
- if (typeof declared !== 'string' || declared.length > 80 || !/^[\^~]?[0-9]+\.[0-9]+\.[0-9]+$/.test(declared)
277
- || declared !== lockedRequest || typeof lockedVersion !== 'string'
278
- || !/^[0-9]+\.[0-9]+\.[0-9]+$/.test(lockedVersion)
279
- || !lockedVersion.startsWith('3.2.')
280
- || typeof lockedViteVersion !== 'string'
281
- || !/^[0-9]+\.[0-9]+\.[0-9]+$/.test(lockedViteVersion)
282
- || !supportsViteRunnerLoader(lockedViteVersion)) {
283
- throw new Error('refactor-root-tests-toolchain-unverified');
284
- }
285
- const nodeModules = realpathSync(join(root, 'node_modules'));
286
- const modulesStat = lstatSync(nodeModules);
287
- if (basename(nodeModules) !== 'node_modules' || !modulesStat.isDirectory()
288
- || modulesStat.isSymbolicLink() || modulesStat.uid !== process.getuid?.()) {
289
- throw new Error('refactor-root-tests-toolchain-unverified');
290
- }
291
- const installedPath = join(nodeModules, 'vitest', 'package.json');
292
- const entryPath = join(nodeModules, 'vitest', 'vitest.mjs');
293
- const vitePath = join(nodeModules, 'vite', 'package.json');
294
- const installedStat = lstatSync(installedPath);
295
- const entryStat = lstatSync(entryPath);
296
- const viteStat = lstatSync(vitePath);
297
- if (!installedStat.isFile() || installedStat.isSymbolicLink()
298
- || !entryStat.isFile() || entryStat.isSymbolicLink()
299
- || !viteStat.isFile() || viteStat.isSymbolicLink()
300
- || realpathSync(installedPath) !== installedPath || realpathSync(entryPath) !== entryPath
301
- || realpathSync(vitePath) !== vitePath
302
- || installedStat.size > 64 * 1024 || entryStat.size > 1024 * 1024
303
- || viteStat.size > 64 * 1024) {
304
- throw new Error('refactor-root-tests-toolchain-unverified');
305
- }
306
- const installedBytes = readFileSync(installedPath);
307
- const entryBytes = readFileSync(entryPath);
308
- const viteBytes = readFileSync(vitePath);
309
- let installed;
310
- let vite;
311
- try {
312
- installed = JSON.parse(installedBytes.toString('utf8'));
313
- vite = JSON.parse(viteBytes.toString('utf8'));
314
- }
315
- catch {
316
- throw new Error('refactor-root-tests-toolchain-unverified');
317
- }
318
- if (installed.name !== 'vitest' || installed.version !== lockedVersion
319
- || vite.name !== 'vite' || vite.version !== lockedViteVersion) {
320
- throw new Error('refactor-root-tests-toolchain-unverified');
321
- }
322
- return { nodeModules, sha256: digest(JSON.stringify([
323
- digest(manifestBytes), digest(lockBytes), digest(installedBytes), digest(entryBytes),
324
- digest(viteBytes),
325
- ])) };
326
- }
327
- function supportsViteRunnerLoader(version) {
328
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
329
- return !!match && Number(match[1]) <= 7 && (Number(match[1]) > 6
330
- || Number(match[1]) === 6 && Number(match[2]) >= 1);
331
- }
332
- function sandboxCharacterizationTests(files, tests, runtime, canary, toolchain, cppToolchain, causalPath) {
333
- if (runtime === 'python') {
334
- const directory = dirname(tests[0]);
335
- const discoverable = ordered([...files.keys()].filter((path) => (directory === '.' || path.startsWith(`${directory}/`))
336
- && /(?:^|\/)test_[A-Za-z0-9_]+\.py$/.test(path)));
337
- if (JSON.stringify(discoverable) !== JSON.stringify(tests)) {
338
- throw new Error('refactor-root-tests-path-invalid');
339
- }
340
- }
341
- const executable = realpathSync(runtime === 'cpp' ? '/usr/bin/dash'
342
- : runtime === 'python' ? '/usr/bin/python3' : process.execPath);
343
- if (runtime === 'node-typescript' && !supportsNativeTypeStrippingRuntime()) {
344
- throw new Error('refactor-root-tests-toolchain-unavailable');
345
- }
346
- if (process.platform !== 'linux' || !existsSync('/usr/bin/bwrap')
347
- || !lstatSync('/usr/bin/bwrap').isFile()
348
- || !(executable.startsWith('/usr/') || executable.startsWith('/home/linuxbrew/.linuxbrew/'))) {
349
- throw new Error('refactor-root-tests-sandbox-unavailable');
350
- }
351
- const scratch = mkdtempSync(join(tmpdir(), 'p222-refactor-characterization-'));
352
- try {
353
- const work = join(scratch, 'work');
354
- mkdirSync(work, { mode: 0o700 });
355
- for (const [path, bytes] of files) {
356
- const target = join(work, path);
357
- mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
358
- writeFileSync(target, bytes, { mode: 0o600, flag: 'wx' });
359
- }
360
- if (runtime === 'vitest') {
361
- if (!toolchain)
362
- throw new Error('refactor-root-tests-toolchain-unavailable');
363
- mkdirSync(join(work, 'node_modules'), { mode: 0o700 });
364
- writeFileSync(join(work, 'p222-vitest.config.mjs'), `export default ${JSON.stringify({ test: { include: tests, environment: 'node',
365
- silent: true, passWithNoTests: false, watch: false, maxWorkers: 1,
366
- fileParallelism: false, pool: 'forks', coverage: { enabled: false } } })};\n`, { mode: 0o600, flag: 'wx' });
367
- }
368
- if (runtime === 'cpp') {
369
- if (!cppToolchain || !causalPath || !files.has(causalPath)) {
370
- throw new Error('refactor-root-tests-toolchain-unavailable');
371
- }
372
- const compiler = cppToolchain.executable;
373
- const sources = ordered([...files.keys()].filter(path => path.endsWith('.cpp')
374
- && path !== causalPath && !tests.includes(path))).map(path => `'/work/${path}'`);
375
- if (canary)
376
- writeFileSync(join(work, 'p222-cpp-causal.cpp'), '#include <cstdio>\n#include <cstdlib>\n'
377
- + `extern "C" void __cyg_profile_func_enter(void*,void*) { std::fputs(${JSON.stringify(canary + '\n')},stderr); std::_Exit(86); }\n`
378
- + 'extern "C" void __cyg_profile_func_exit(void*,void*) {}\n', { mode: 0o600, flag: 'wx' });
379
- const script = ['set -eu', 'ulimit -c 0',
380
- `'${compiler}' -std=c++17 -O0 -UNDEBUG -I/work ${canary
381
- ? '-finstrument-functions -finstrument-functions-exclude-file-list=/usr/include ' : ''}`
382
- + `-c '/work/${causalPath}' -o /tmp/p222-cpp-causal.o`,
383
- ...tests.flatMap((test, index) => [
384
- `'${compiler}' -std=c++17 -O0 -UNDEBUG -I/work '/work/${test}' ${sources.join(' ')} `
385
- + `/tmp/p222-cpp-causal.o ${canary ? '/work/p222-cpp-causal.cpp ' : ''}-o /tmp/p222-cpp-test-${index}`,
386
- `/tmp/p222-cpp-test-${index}`,
387
- `printf 'P222_CPP_TEST_PASSED_${index}\\n'`,
388
- ]),
389
- ].join('\n') + '\n';
390
- writeFileSync(join(work, 'p222-cpp-runner.sh'), script, { mode: 0o600, flag: 'wx' });
391
- }
392
- const etc = join(scratch, 'etc');
393
- mkdirSync(etc, { mode: 0o700 });
394
- writeFileSync(join(etc, 'passwd'), 'nobody:x:65534:65534:nobody:/tmp:/bin/sh\n', { mode: 0o600 });
395
- writeFileSync(join(etc, 'group'), 'nogroup:x:65534:\n', { mode: 0o600 });
396
- writeFileSync(join(etc, 'hosts'), '127.0.0.1 localhost\n', { mode: 0o600 });
397
- writeFileSync(join(etc, 'nsswitch.conf'), 'hosts: files\n', { mode: 0o600 });
398
- const args = ['--die-with-parent', '--new-session', '--unshare-all', '--clearenv',
399
- '--setenv', 'HOME', '/tmp', '--setenv', 'TMPDIR', '/tmp',
400
- '--setenv', 'PATH', '/home/linuxbrew/.linuxbrew/bin:/usr/bin:/bin',
401
- '--setenv', 'CI', 'true', '--ro-bind', '/usr', '/usr'];
402
- if (runtime === 'python')
403
- args.push('--setenv', 'PYTHONPATH', '/work');
404
- for (const path of ['/lib', '/lib64', '/bin']) {
405
- if (existsSync(path))
406
- args.push('--ro-bind', path, path);
407
- }
408
- if (existsSync('/home/linuxbrew/.linuxbrew')) {
409
- args.push('--ro-bind', '/home/linuxbrew/.linuxbrew', '/home/linuxbrew/.linuxbrew');
410
- }
411
- args.push('--dir', '/etc');
412
- for (const file of ['passwd', 'group', 'hosts', 'nsswitch.conf']) {
413
- args.push('--ro-bind', join(etc, file), `/etc/${file}`);
414
- }
415
- args.push('--dev', '/dev', '--proc', '/proc', '--tmpfs', '/tmp', '--ro-bind', work, '/work');
416
- if (runtime === 'vitest')
417
- args.push('--ro-bind', toolchain.nodeModules, '/work/node_modules');
418
- args.push('--chdir', '/work', '--', executable);
419
- if (runtime === 'cpp') {
420
- args.push('/work/p222-cpp-runner.sh');
421
- }
422
- else if (runtime === 'python') {
423
- // A literal discovery directory and fixed pattern run only the admitted
424
- // test files copied into the read-only snapshot. No project runner,
425
- // site-packages or shell-controlled command line enters this boundary.
426
- args.push('-B', '-S', '-m', 'unittest', 'discover', '-s', dirname(tests[0]), '-p', 'test_*.py');
427
- }
428
- else if (runtime === 'vitest') {
429
- args.push('/work/node_modules/vitest/vitest.mjs', 'run', '--root', '/work', '--config', '/work/p222-vitest.config.mjs', '--configLoader=runner', '--reporter=json', '--no-cache', '--no-file-parallelism', '--maxWorkers=1');
430
- }
431
- else {
432
- if (runtime === 'node-typescript')
433
- args.push('--experimental-strip-types');
434
- args.push('--test', '--test-reporter=tap', ...tests);
435
- }
436
- const result = spawnSync('/usr/bin/bwrap', args, {
437
- env: { PATH: '/usr/bin:/bin' }, encoding: 'buffer',
438
- timeout: 180_000, maxBuffer: 4 * 1024 * 1024,
439
- });
440
- if (result.error || result.signal || result.status === null) {
441
- throw new Error('refactor-root-tests-sandbox-unavailable');
442
- }
443
- const output = Buffer.concat([result.stdout ?? Buffer.alloc(0), result.stderr ?? Buffer.alloc(0)]);
444
- const pythonTestsObserved = runtime !== 'python'
445
- || /^Ran [1-9][0-9]* tests? in /m.test(output.toString('utf8'));
446
- let vitestTestsObserved = true;
447
- if (runtime === 'vitest') {
448
- try {
449
- const report = JSON.parse((result.stdout ?? Buffer.alloc(0)).toString('utf8'));
450
- vitestTestsObserved = Number.isSafeInteger(report.numPassedTests)
451
- && Number(report.numPassedTests) > 0
452
- && report.numTotalTests === report.numPassedTests
453
- && report.numFailedTests === 0;
454
- }
455
- catch {
456
- vitestTestsObserved = false;
457
- }
458
- }
459
- const cppTestsObserved = runtime !== 'cpp' || tests.every((_, index) => output.toString('utf8').split('\n').includes(`P222_CPP_TEST_PASSED_${index}`));
460
- return { passed: result.status === 0 && pythonTestsObserved && vitestTestsObserved && cppTestsObserved,
461
- outputSha256: digest(output),
462
- canaryObserved: canary !== undefined && (runtime === 'cpp'
463
- ? output.toString('utf8').split('\n').includes(canary) : output.includes(Buffer.from(canary))) };
464
- }
465
- finally {
466
- rmSync(scratch, { recursive: true, force: true });
467
- }
468
- }
469
- export function runRootRefactorCharacterization(projectRoot, admissionSha256, testPaths, dependencyPaths, phase, authorizeRun, causalPath, baselineSha256, runner = 'native') {
470
- if (!authorizeRun)
471
- throw new Error('refactor-root-run-authorization-required');
472
- if (testPaths.length < 1 || testPaths.length > MAX_TESTS || dependencyPaths.length > MAX_DEPENDENCIES
473
- || !['native', 'vitest', 'cpp'].includes(runner)
474
- || (phase === 'baseline' && baselineSha256 !== undefined)
475
- || (phase === 'after' && (!baselineSha256 || !SHA256.test(baselineSha256)))) {
476
- throw new Error('refactor-root-tests-arguments-invalid');
477
- }
478
- const tests = ordered(testPaths);
479
- const dependencies = ordered(dependencyPaths);
480
- const runtime = runner === 'cpp' ? 'cpp' : runner === 'vitest' ? 'vitest'
481
- : tests.every((path) => /(?:^|\/)test_[A-Za-z0-9_]+\.py$/.test(path))
482
- && causalPath.endsWith('.py') ? 'python'
483
- : tests.every((path) => path.endsWith('.test.ts')) && causalPath.endsWith('.ts')
484
- ? 'node-typescript' : 'node';
485
- if (new Set([...tests, ...dependencies]).size !== tests.length + dependencies.length
486
- || tests.some((path) => !safePath(path) || (runtime === 'cpp' ? !path.endsWith('.test.cpp') : runtime === 'python'
487
- ? !/(?:^|\/)test_[A-Za-z0-9_]+\.py$/.test(path) || dirname(path) !== dirname(tests[0])
488
- : runtime === 'vitest' ? !/\.test\.(?:ts|js|mjs)$/.test(path)
489
- : runtime === 'node-typescript' ? !path.endsWith('.test.ts')
490
- : !/\.test\.(?:mjs|cjs|js)$/.test(path)))
491
- || !safePath(causalPath) || (runtime === 'cpp'
492
- ? !causalPath.endsWith('.cpp') || causalPath.endsWith('.test.cpp') : runtime === 'python'
493
- ? !causalPath.endsWith('.py') : runtime === 'vitest'
494
- ? !/\.(?:ts|js|mjs)$/.test(causalPath) || /\.test\./.test(causalPath)
495
- : runtime === 'node-typescript'
496
- ? !causalPath.endsWith('.ts') || causalPath.endsWith('.test.ts')
497
- : !/\.(?:mjs|cjs|js)$/.test(causalPath))
498
- || dependencies.some((path) => !safePath(path) || runtime === 'python'
499
- && /(?:^|\/)test_[A-Za-z0-9_]+\.py$/.test(path))) {
500
- throw new Error('refactor-root-tests-path-invalid');
501
- }
502
- const root = resolve(projectRoot);
503
- try {
504
- const record = readPrivateRefactorAdmission(root, admissionSha256);
505
- const { pendingDocumentationPaths } = verifyRootRefactorForCharacterization(root, admissionSha256);
506
- const admitted = new Set(admittedPaths(record));
507
- if (tests.some((path) => !admitted.has(path))
508
- || !record.files.some((file) => file.path === causalPath)
509
- || tests.includes(causalPath)
510
- || dependencies.some((path) => admitted.has(path))) {
511
- throw new Error('refactor-root-tests-path-invalid');
512
- }
513
- for (const path of [...tests, causalPath, ...dependencies]) {
514
- requireExplicitJsModuleType(root, path, dependencies);
515
- }
516
- for (const path of dependencies)
517
- exactCommittedRefactorFile(root, path);
518
- const toolchain = runtime === 'vitest'
519
- ? exactVitestToolchain(root, tests, dependencies) : undefined;
520
- const cppToolchain = runtime === 'cpp' ? exactCppToolchain() : undefined;
521
- const toolchainSha256 = toolchain?.sha256 ?? cppToolchain?.sha256;
522
- const changed = mutationQaGit(root, ['diff', '--name-only', '-z', '--no-ext-diff', '--no-textconv'])
523
- .toString('utf8').split('\0').filter((path) => path && !path.startsWith('.neocortex/'));
524
- const untracked = mutationQaGit(root, ['ls-files', '--others', '--exclude-standard', '-z'])
525
- .toString('utf8').split('\0').filter((path) => path && !path.startsWith('.neocortex/'));
526
- const changedOutsideTests = [...changed, ...untracked].filter((path) => !tests.includes(path));
527
- if ((phase === 'baseline' && changedOutsideTests.length > 0)
528
- || (phase === 'after' && changedOutsideTests.length === 0)) {
529
- throw new Error('refactor-root-tests-phase-invalid');
530
- }
531
- const testFiles = tests.map((path) => ({ path, sha256: digest(physicalBytes(root, path)) }));
532
- const dependencyFiles = dependencies.map((path) => ({ path, sha256: digest(physicalBytes(root, path)) }));
533
- const baseline = phase === 'after' ? readRootRefactorBaseline(root, baselineSha256) : null;
534
- if (baseline && (baseline.admissionSha256 !== admissionSha256
535
- || baseline.sourceHead !== record.sourceHead
536
- || baseline.causalPath !== causalPath
537
- || (baseline.schemaVersion === 2 ? runner !== baseline.runner
538
- || baseline.toolchainSha256 !== toolchainSha256 : runner !== 'native')
539
- || JSON.stringify(baseline.testFiles) !== JSON.stringify(testFiles)
540
- || JSON.stringify(baseline.dependencies) !== JSON.stringify(dependencyFiles))) {
541
- throw new Error('refactor-root-baseline-stale');
542
- }
543
- const before = snapshot(root, record, dependencies, pendingDocumentationPaths);
544
- if (baseline && digest(before.files.get(causalPath))
545
- === record.files.find((file) => file.path === causalPath).sha256) {
546
- throw new Error('refactor-root-tests-phase-invalid');
547
- }
548
- const execution = sandboxCharacterizationTests(before.files, tests, runtime, undefined, toolchain, cppToolchain, causalPath);
549
- let causalOutputSha256 = null;
550
- let causalFailureObserved = false;
551
- if (execution.passed) {
552
- const canary = `P222_REFACTOR_CAUSAL_${randomBytes(16).toString('hex')}`;
553
- const causalFiles = new Map(before.files);
554
- if (runtime !== 'cpp')
555
- causalFiles.set(causalPath, Buffer.concat([
556
- Buffer.from(runtime === 'python'
557
- ? `raise RuntimeError(${JSON.stringify(canary)})\n`
558
- : `throw new Error(${JSON.stringify(canary)});\n`),
559
- before.files.get(causalPath),
560
- ]));
561
- const causal = sandboxCharacterizationTests(causalFiles, tests, runtime, canary, toolchain, cppToolchain, causalPath);
562
- causalOutputSha256 = causal.outputSha256;
563
- causalFailureObserved = !causal.passed && causal.canaryObserved;
564
- }
565
- const revalidated = verifyRootRefactorForCharacterization(root, admissionSha256);
566
- if (JSON.stringify(revalidated.pendingDocumentationPaths) !== JSON.stringify(pendingDocumentationPaths)) {
567
- throw new Error('refactor-root-tests-source-stale');
568
- }
569
- const after = snapshot(root, record, dependencies, pendingDocumentationPaths);
570
- if (cppToolchain && exactCppToolchain().sha256 !== cppToolchain.sha256) {
571
- throw new Error('refactor-root-tests-toolchain-stale');
572
- }
573
- if (before.candidateSha256 !== after.candidateSha256) {
574
- throw new Error('refactor-root-tests-source-stale');
575
- }
576
- let savedBaseline = baselineSha256 ?? null;
577
- if (phase === 'baseline' && execution.passed && causalFailureObserved && causalOutputSha256) {
578
- savedBaseline = persistBaseline(root, {
579
- ...(runtime === 'vitest' || runtime === 'cpp'
580
- ? { schemaVersion: 2, runner: runtime,
581
- toolchainSha256: toolchainSha256 }
582
- : { schemaVersion: 1 }),
583
- kind: 'root-refactor-characterization-baseline',
584
- admissionSha256, sourceHead: record.sourceHead,
585
- testFiles, dependencies: dependencyFiles, causalPath,
586
- candidateSha256: before.candidateSha256,
587
- outputSha256: execution.outputSha256, causalOutputSha256,
588
- });
589
- }
590
- return Object.freeze({ schemaVersion: 1, kind: 'root-refactor-characterization-observation',
591
- phase, admissionSha256, baselineSha256: savedBaseline,
592
- candidateSha256: before.candidateSha256, outputSha256: execution.outputSha256,
593
- causalOutputSha256,
594
- testFileCount: tests.length, dependencyCount: dependencies.length,
595
- testsPassedObserved: execution.passed, causalFailureObserved, qaAccepted: false,
596
- gitCommitAuthorized: false, publicSafe: true });
597
- }
598
- catch (error) {
599
- if (error instanceof Error && error.message.startsWith('refactor-root-'))
600
- throw error;
601
- throw new Error('refactor-root-tests-unverified');
602
- }
603
- }
604
- export function refactorCharacterizationCliHandler(args) {
605
- let projectRoot = '';
606
- let admissionSha256 = '';
607
- let baselineSha256;
608
- let phase = '';
609
- let authorizeRun = false;
610
- let causalPath = '';
611
- let runner = 'native';
612
- let format = 'plain';
613
- const tests = [];
614
- const dependencies = [];
615
- try {
616
- if (args[0] !== 'characterize')
617
- throw new Error('refactor-root-tests-arguments-invalid');
618
- for (let index = 1; index < args.length; index++) {
619
- const token = args[index];
620
- const next = () => {
621
- const value = args[++index];
622
- if (!value || value.startsWith('--'))
623
- throw new Error('refactor-root-tests-arguments-invalid');
624
- return value;
625
- };
626
- if (token === '--project-root' && !projectRoot)
627
- projectRoot = next();
628
- else if (token === '--admission-sha' && !admissionSha256)
629
- admissionSha256 = next();
630
- else if (token === '--baseline-sha' && !baselineSha256)
631
- baselineSha256 = next();
632
- else if (token === '--phase' && !phase) {
633
- const value = next();
634
- if (value !== 'baseline' && value !== 'after')
635
- throw new Error('refactor-root-tests-arguments-invalid');
636
- phase = value;
637
- }
638
- else if (token === '--test-path')
639
- tests.push(next());
640
- else if (token === '--dependency')
641
- dependencies.push(next());
642
- else if (token === '--causal-path' && !causalPath)
643
- causalPath = next();
644
- else if (token === '--runner' && runner === 'native') {
645
- const value = next();
646
- if (value !== 'vitest' && value !== 'cpp')
647
- throw new Error('refactor-root-tests-arguments-invalid');
648
- runner = value;
649
- }
650
- else if (token === '--authorize-run' && !authorizeRun)
651
- authorizeRun = true;
652
- else if (token === '--format' && format === 'plain')
653
- format = next();
654
- else
655
- throw new Error('refactor-root-tests-arguments-invalid');
656
- }
657
- if (!projectRoot || !admissionSha256 || !phase || !causalPath || !['plain', 'json'].includes(format)) {
658
- throw new Error('refactor-root-tests-arguments-invalid');
659
- }
660
- const observation = runRootRefactorCharacterization(projectRoot, admissionSha256, tests, dependencies, phase, authorizeRun, causalPath, baselineSha256, runner);
661
- const ok = observation.testsPassedObserved && observation.causalFailureObserved;
662
- process.stdout.write(`${JSON.stringify({ ok, observation })}\n`);
663
- return ok ? 0 : 1;
664
- }
665
- catch (error) {
666
- const reasonCode = error instanceof Error && /^refactor-root-[a-z-]+$/.test(error.message)
667
- ? error.message : 'refactor-root-tests-unverified';
668
- process.stdout.write(`${JSON.stringify({ ok: false, reasonCode })}\n`);
669
- return 1;
670
- }
671
- }
1
+ import{spawnSync as G}from"node:child_process";import{createHash as lt,randomBytes as dt}from"node:crypto";import{closeSync as Q,constants as R,existsSync as C,fsyncSync as K,lstatSync as $,mkdirSync as Z,mkdtempSync as ut,openSync as tt,readFileSync as H,realpathSync as W,rmSync as pt,writeFileSync as z}from"node:fs";import{tmpdir as ht}from"node:os";import{basename as mt,dirname as N,join as y,resolve as wt}from"node:path";import{mutationQaGit as et}from"./mutation-qa-git.js";import{exactCommittedRefactorFile as vt,privateRefactorDirectory as rt,readPrivateRefactorAdmission as yt,verifyRootRefactorForCharacterization as ot}from"./refactor-root.js";const A=/^[a-f0-9]{64}$/,gt=64*1024*1024,st=32,nt=128;function bt(e){const t=e.match(/^(\d+)\.(\d+)\.(\d+)$/);if(!t)return!1;const r=Number(t[1]),n=Number(t[2]);return r>22||r===22&&n>=6}function St(){return bt(process.versions.node)&&process.config.variables.node_use_amaro===!0}function b(e){return lt("sha256").update(e).digest("hex")}function D(e){return e.length>0&&e.length<=512&&/^[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)*$/.test(e)&&e.split("/").every(t=>t!=="."&&t!==".."&&![".git",".neocortex","node_modules"].includes(t.toLowerCase()))}function V(e){return[...e].sort((t,r)=>Buffer.from(t).compare(Buffer.from(r)))}function Et(e,t,r){if(!t.endsWith(".js")&&!t.endsWith(".ts"))return;let n=N(t);for(;;){const f=n==="."?"package.json":`${n}/package.json`;if(C(y(e,f))){if(!r.includes(f))throw new Error("refactor-root-tests-manifest-unbound");let s;try{s=JSON.parse(L(e,f).toString("utf8"))}catch{throw new Error("refactor-root-tests-manifest-invalid")}if(!s||typeof s!="object"||Array.isArray(s)||!["module","commonjs"].includes(s.type)||t.endsWith(".ts")&&s.type!=="module")throw new Error("refactor-root-tests-manifest-invalid");return}if(n===".")break;n=N(n)}throw new Error("refactor-root-tests-manifest-missing")}function L(e,t){if(!D(t))throw new Error("refactor-root-tests-path-invalid");const r=y(e,t),n=$(r);if(W(r)!==r||!n.isFile()||n.isSymbolicLink()||n.size<1||n.size>8*1024*1024)throw new Error("refactor-root-tests-source-unverified");const f=H(r);if(f.length<1||f.length>8*1024*1024||f.includes(0))throw new Error("refactor-root-tests-source-unverified");return new TextDecoder("utf-8",{fatal:!0}).decode(f),f}function q(e,t){if(!A.test(t))throw new Error("refactor-root-baseline-invalid");return y(rt(e),`characterization-baseline-${t}.json`)}function Y(e,t){const r=$(q(e,t));if(!r.isFile()||r.isSymbolicLink()||r.uid!==process.getuid?.()||(r.mode&63)!==0||r.size<100||r.size>64*1024)throw new Error("refactor-root-baseline-invalid");const n=H(q(e,t));if(n.length>64*1024)throw new Error("refactor-root-baseline-invalid");let f;try{f=JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(n))}catch{throw new Error("refactor-root-baseline-invalid")}if(!f||typeof f!="object"||Array.isArray(f))throw new Error("refactor-root-baseline-invalid");const s=f,a=["schemaVersion","kind","admissionSha256","sourceHead","testFiles","dependencies","causalPath","candidateSha256","outputSha256","causalOutputSha256","baselineSha256"],l=o=>Array.isArray(o)&&o.every(p=>p&&typeof p=="object"&&!Array.isArray(p)&&Object.keys(p).sort().join("\0")==="path\0sha256"&&typeof p.path=="string"&&typeof p.sha256=="string"&&D(p.path)&&A.test(p.sha256)),w=s.schemaVersion===2;if(Object.keys(s).sort().join("\0")!==(w?[...a,"runner","toolchainSha256"]:a).sort().join("\0")||s.schemaVersion!==1&&s.schemaVersion!==2||w&&(!["vitest","cpp"].includes(String(s.runner))||!A.test(String(s.toolchainSha256)))||s.kind!=="root-refactor-characterization-baseline"||!A.test(String(s.admissionSha256))||typeof s.sourceHead!="string"||!/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(s.sourceHead)||!l(s.testFiles)||!l(s.dependencies)||s.testFiles.length<1||s.testFiles.length>st||s.dependencies.length>nt||typeof s.causalPath!="string"||!D(s.causalPath)||!A.test(String(s.candidateSha256))||!A.test(String(s.outputSha256))||!A.test(String(s.causalOutputSha256))||s.baselineSha256!==t)throw new Error("refactor-root-baseline-invalid");const c=s.testFiles,d=s.dependencies;if(new Set([...c,...d].map(o=>o.path)).size!==c.length+d.length||c.map(o=>o.path).join("\0")!==V(c.map(o=>o.path)).join("\0")||d.map(o=>o.path).join("\0")!==V(d.map(o=>o.path)).join("\0"))throw new Error("refactor-root-baseline-invalid");const{baselineSha256:i,...u}=s;if(b(JSON.stringify(u))!==t||!n.equals(Buffer.from(`${JSON.stringify(s)}
2
+ `)))throw new Error("refactor-root-baseline-invalid");return s}function kt(e,t){const r=b(JSON.stringify(t)),n=q(e,r),f={...t,baselineSha256:r},s=Buffer.from(`${JSON.stringify(f)}
3
+ `);if(C(n))Y(e,r);else{const a=tt(n,R.O_WRONLY|R.O_CREAT|R.O_EXCL|(R.O_NOFOLLOW??0),384);try{z(a,s),K(a)}finally{Q(a)}const l=tt(rt(e),R.O_RDONLY|(R.O_DIRECTORY??0));try{K(l)}finally{Q(l)}}return Y(e,r),r}function it(e){return[...e.files.map(t=>t.path),...e.schemaVersion===2?e.newFiles.map(t=>t.path):[]]}function at(e,t,r,n=[]){const f=new Map;let s=0;for(const l of[...it(t),...r]){if(n.includes(l)){try{$(y(e,l))}catch(c){if(c.code==="ENOENT")continue;throw c}throw new Error("refactor-root-tests-source-stale")}const w=L(e,l);if(s+=w.length,s>gt)throw new Error("refactor-root-tests-snapshot-too-large");f.set(l,w)}const a=b(JSON.stringify([...[...f].map(([l,w])=>[l,b(w)]),...n.map(l=>[l,null])]));return{files:f,candidateSha256:a}}function ct(){try{const e=W("/usr/bin/g++"),t=$(e);if(!e.startsWith("/usr/")||!/^[A-Za-z0-9_./+-]+$/.test(e)||!t.isFile()||t.uid!==0||(t.mode&18)!==0||t.size<1||t.size>32*1024*1024)throw new Error("invalid compiler");const r=G(e,["-dumpfullversion","-dumpversion"],{env:{PATH:"/usr/bin:/bin"},encoding:"utf8",timeout:1e4,maxBuffer:4096}),n=r.stdout?.trim();if(r.status!==0||!/^(?:11|12|13|14|15)\.[0-9]+\.[0-9]+$/.test(n))throw new Error("unsupported compiler");return{executable:e,sha256:b(JSON.stringify([e,n,b(H(e))]))}}catch{throw new Error("refactor-root-tests-toolchain-unverified")}}function Ot(e,t,r){let n="";for(const M of t){let J=N(M),F="";for(;;){const I=J==="."?"package.json":`${J}/package.json`;if(C(y(e,I))){F=I;break}if(J===".")break;J=N(J)}if(!F||n&&F!==n)throw new Error("refactor-root-tests-toolchain-unverified");n=F}if(!r.includes(n)||!r.includes("package-lock.json"))throw new Error("refactor-root-tests-toolchain-unbound");const f=L(e,n),s=L(e,"package-lock.json");let a,l;try{a=JSON.parse(f.toString("utf8")),l=JSON.parse(s.toString("utf8"))}catch{throw new Error("refactor-root-tests-toolchain-unverified")}const w=a.devDependencies?.vitest??a.dependencies?.vitest,c=l.packages,d=n==="package.json"?"":N(n),i=c?.[d],u=i?.devDependencies?.vitest??i?.dependencies?.vitest,o=c?.["node_modules/vitest"]?.version,p=c?.["node_modules/vite"]?.version;if(typeof w!="string"||w.length>80||!/^[\^~]?[0-9]+\.[0-9]+\.[0-9]+$/.test(w)||w!==u||typeof o!="string"||!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(o)||!o.startsWith("3.2.")||typeof p!="string"||!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(p)||!_t(p))throw new Error("refactor-root-tests-toolchain-unverified");const v=W(y(e,"node_modules")),g=$(v);if(mt(v)!=="node_modules"||!g.isDirectory()||g.isSymbolicLink()||g.uid!==process.getuid?.())throw new Error("refactor-root-tests-toolchain-unverified");const h=y(v,"vitest","package.json"),S=y(v,"vitest","vitest.mjs"),O=y(v,"vite","package.json"),E=$(h),_=$(S),B=$(O);if(!E.isFile()||E.isSymbolicLink()||!_.isFile()||_.isSymbolicLink()||!B.isFile()||B.isSymbolicLink()||W(h)!==h||W(S)!==S||W(O)!==O||E.size>64*1024||_.size>1024*1024||B.size>64*1024)throw new Error("refactor-root-tests-toolchain-unverified");const P=H(h),k=H(S),x=H(O);let j,T;try{j=JSON.parse(P.toString("utf8")),T=JSON.parse(x.toString("utf8"))}catch{throw new Error("refactor-root-tests-toolchain-unverified")}if(j.name!=="vitest"||j.version!==o||T.name!=="vite"||T.version!==p)throw new Error("refactor-root-tests-toolchain-unverified");return{nodeModules:v,sha256:b(JSON.stringify([b(f),b(s),b(P),b(k),b(x)]))}}function _t(e){const t=e.match(/^(\d+)\.(\d+)\.(\d+)$/);return!!t&&Number(t[1])<=7&&(Number(t[1])>6||Number(t[1])===6&&Number(t[2])>=1)}function ft(e,t,r,n,f,s,a){if(r==="python"){const c=N(t[0]),d=V([...e.keys()].filter(i=>(c==="."||i.startsWith(`${c}/`))&&/(?:^|\/)test_[A-Za-z0-9_]+\.py$/.test(i)));if(JSON.stringify(d)!==JSON.stringify(t))throw new Error("refactor-root-tests-path-invalid")}const l=W(r==="cpp"?"/usr/bin/dash":r==="python"?"/usr/bin/python3":process.execPath);if(r==="node-typescript"&&!St())throw new Error("refactor-root-tests-toolchain-unavailable");if(process.platform!=="linux"||!C("/usr/bin/bwrap")||!$("/usr/bin/bwrap").isFile()||!(l.startsWith("/usr/")||l.startsWith("/home/linuxbrew/.linuxbrew/")))throw new Error("refactor-root-tests-sandbox-unavailable");const w=ut(y(ht(),"p222-refactor-characterization-"));try{const c=y(w,"work");Z(c,{mode:448});for(const[h,S]of e){const O=y(c,h);Z(N(O),{recursive:!0,mode:448}),z(O,S,{mode:384,flag:"wx"})}if(r==="vitest"){if(!f)throw new Error("refactor-root-tests-toolchain-unavailable");Z(y(c,"node_modules"),{mode:448}),z(y(c,"p222-vitest.config.mjs"),`export default ${JSON.stringify({test:{include:t,environment:"node",silent:!0,passWithNoTests:!1,watch:!1,maxWorkers:1,fileParallelism:!1,pool:"forks",coverage:{enabled:!1}}})};
4
+ `,{mode:384,flag:"wx"})}if(r==="cpp"){if(!s||!a||!e.has(a))throw new Error("refactor-root-tests-toolchain-unavailable");const h=s.executable,S=V([...e.keys()].filter(E=>E.endsWith(".cpp")&&E!==a&&!t.includes(E))).map(E=>`'/work/${E}'`);n&&z(y(c,"p222-cpp-causal.cpp"),`#include <cstdio>
5
+ #include <cstdlib>
6
+ extern "C" void __cyg_profile_func_enter(void*,void*) { std::fputs(${JSON.stringify(n+`
7
+ `)},stderr); std::_Exit(86); }
8
+ extern "C" void __cyg_profile_func_exit(void*,void*) {}
9
+ `,{mode:384,flag:"wx"});const O=["set -eu","ulimit -c 0",`'${h}' -std=c++17 -O0 -UNDEBUG -I/work ${n?"-finstrument-functions -finstrument-functions-exclude-file-list=/usr/include ":""}-c '/work/${a}' -o /tmp/p222-cpp-causal.o`,...t.flatMap((E,_)=>[`'${h}' -std=c++17 -O0 -UNDEBUG -I/work '/work/${E}' ${S.join(" ")} /tmp/p222-cpp-causal.o ${n?"/work/p222-cpp-causal.cpp ":""}-o /tmp/p222-cpp-test-${_}`,`/tmp/p222-cpp-test-${_}`,`printf 'P222_CPP_TEST_PASSED_${_}\\n'`])].join(`
10
+ `)+`
11
+ `;z(y(c,"p222-cpp-runner.sh"),O,{mode:384,flag:"wx"})}const d=y(w,"etc");Z(d,{mode:448}),z(y(d,"passwd"),`nobody:x:65534:65534:nobody:/tmp:/bin/sh
12
+ `,{mode:384}),z(y(d,"group"),`nogroup:x:65534:
13
+ `,{mode:384}),z(y(d,"hosts"),`127.0.0.1 localhost
14
+ `,{mode:384}),z(y(d,"nsswitch.conf"),`hosts: files
15
+ `,{mode:384});const i=["--die-with-parent","--new-session","--unshare-all","--clearenv","--setenv","HOME","/tmp","--setenv","TMPDIR","/tmp","--setenv","PATH","/home/linuxbrew/.linuxbrew/bin:/usr/bin:/bin","--setenv","CI","true","--ro-bind","/usr","/usr"];r==="python"&&i.push("--setenv","PYTHONPATH","/work");for(const h of["/lib","/lib64","/bin"])C(h)&&i.push("--ro-bind",h,h);C("/home/linuxbrew/.linuxbrew")&&i.push("--ro-bind","/home/linuxbrew/.linuxbrew","/home/linuxbrew/.linuxbrew"),i.push("--dir","/etc");for(const h of["passwd","group","hosts","nsswitch.conf"])i.push("--ro-bind",y(d,h),`/etc/${h}`);i.push("--dev","/dev","--proc","/proc","--tmpfs","/tmp","--ro-bind",c,"/work"),r==="vitest"&&i.push("--ro-bind",f.nodeModules,"/work/node_modules"),i.push("--chdir","/work","--",l),r==="cpp"?i.push("/work/p222-cpp-runner.sh"):r==="python"?i.push("-B","-S","-m","unittest","discover","-s",N(t[0]),"-p","test_*.py"):r==="vitest"?i.push("/work/node_modules/vitest/vitest.mjs","run","--root","/work","--config","/work/p222-vitest.config.mjs","--configLoader=runner","--reporter=json","--no-cache","--no-file-parallelism","--maxWorkers=1"):(r==="node-typescript"&&i.push("--experimental-strip-types"),i.push("--test","--test-reporter=tap",...t));const u=G("/usr/bin/bwrap",i,{env:{PATH:"/usr/bin:/bin"},encoding:"buffer",timeout:18e4,maxBuffer:4*1024*1024});if(u.error||u.signal||u.status===null)throw new Error("refactor-root-tests-sandbox-unavailable");const o=Buffer.concat([u.stdout??Buffer.alloc(0),u.stderr??Buffer.alloc(0)]),p=r!=="python"||/^Ran [1-9][0-9]* tests? in /m.test(o.toString("utf8"));let v=!0;if(r==="vitest")try{const h=JSON.parse((u.stdout??Buffer.alloc(0)).toString("utf8"));v=Number.isSafeInteger(h.numPassedTests)&&Number(h.numPassedTests)>0&&h.numTotalTests===h.numPassedTests&&h.numFailedTests===0}catch{v=!1}const g=r!=="cpp"||t.every((h,S)=>o.toString("utf8").split(`
16
+ `).includes(`P222_CPP_TEST_PASSED_${S}`));return{passed:u.status===0&&p&&v&&g,outputSha256:b(o),canaryObserved:n!==void 0&&(r==="cpp"?o.toString("utf8").split(`
17
+ `).includes(n):o.includes(Buffer.from(n)))}}finally{pt(w,{recursive:!0,force:!0})}}function xt(e,t,r,n,f,s,a,l,w="native"){if(!s)throw new Error("refactor-root-run-authorization-required");if(r.length<1||r.length>st||n.length>nt||!["native","vitest","cpp"].includes(w)||f==="baseline"&&l!==void 0||f==="after"&&(!l||!A.test(l)))throw new Error("refactor-root-tests-arguments-invalid");const c=V(r),d=V(n),i=w==="cpp"?"cpp":w==="vitest"?"vitest":c.every(o=>/(?:^|\/)test_[A-Za-z0-9_]+\.py$/.test(o))&&a.endsWith(".py")?"python":c.every(o=>o.endsWith(".test.ts"))&&a.endsWith(".ts")?"node-typescript":"node";if(new Set([...c,...d]).size!==c.length+d.length||c.some(o=>!D(o)||(i==="cpp"?!o.endsWith(".test.cpp"):i==="python"?!/(?:^|\/)test_[A-Za-z0-9_]+\.py$/.test(o)||N(o)!==N(c[0]):i==="vitest"?!/\.test\.(?:ts|js|mjs)$/.test(o):i==="node-typescript"?!o.endsWith(".test.ts"):!/\.test\.(?:mjs|cjs|js)$/.test(o)))||!D(a)||(i==="cpp"?!a.endsWith(".cpp")||a.endsWith(".test.cpp"):i==="python"?!a.endsWith(".py"):i==="vitest"?!/\.(?:ts|js|mjs)$/.test(a)||/\.test\./.test(a):i==="node-typescript"?!a.endsWith(".ts")||a.endsWith(".test.ts"):!/\.(?:mjs|cjs|js)$/.test(a))||d.some(o=>!D(o)||i==="python"&&/(?:^|\/)test_[A-Za-z0-9_]+\.py$/.test(o)))throw new Error("refactor-root-tests-path-invalid");const u=wt(e);try{const o=yt(u,t),{pendingDocumentationPaths:p}=ot(u,t),v=new Set(it(o));if(c.some(m=>!v.has(m))||!o.files.some(m=>m.path===a)||c.includes(a)||d.some(m=>v.has(m)))throw new Error("refactor-root-tests-path-invalid");for(const m of[...c,a,...d])Et(u,m,d);for(const m of d)vt(u,m);const g=i==="vitest"?Ot(u,c,d):void 0,h=i==="cpp"?ct():void 0,S=g?.sha256??h?.sha256,O=et(u,["diff","--name-only","-z","--no-ext-diff","--no-textconv"]).toString("utf8").split("\0").filter(m=>m&&!m.startsWith(".neocortex/")),E=et(u,["ls-files","--others","--exclude-standard","-z"]).toString("utf8").split("\0").filter(m=>m&&!m.startsWith(".neocortex/")),_=[...O,...E].filter(m=>!c.includes(m));if(f==="baseline"&&_.length>0||f==="after"&&_.length===0)throw new Error("refactor-root-tests-phase-invalid");const B=c.map(m=>({path:m,sha256:b(L(u,m))})),P=d.map(m=>({path:m,sha256:b(L(u,m))})),k=f==="after"?Y(u,l):null;if(k&&(k.admissionSha256!==t||k.sourceHead!==o.sourceHead||k.causalPath!==a||(k.schemaVersion===2?w!==k.runner||k.toolchainSha256!==S:w!=="native")||JSON.stringify(k.testFiles)!==JSON.stringify(B)||JSON.stringify(k.dependencies)!==JSON.stringify(P)))throw new Error("refactor-root-baseline-stale");const x=at(u,o,d,p);if(k&&b(x.files.get(a))===o.files.find(m=>m.path===a).sha256)throw new Error("refactor-root-tests-phase-invalid");const j=ft(x.files,c,i,void 0,g,h,a);let T=null,M=!1;if(j.passed){const m=`P222_REFACTOR_CAUSAL_${dt(16).toString("hex")}`,X=new Map(x.files);i!=="cpp"&&X.set(a,Buffer.concat([Buffer.from(i==="python"?`raise RuntimeError(${JSON.stringify(m)})
18
+ `:`throw new Error(${JSON.stringify(m)});
19
+ `),x.files.get(a)]));const U=ft(X,c,i,m,g,h,a);T=U.outputSha256,M=!U.passed&&U.canaryObserved}const J=ot(u,t);if(JSON.stringify(J.pendingDocumentationPaths)!==JSON.stringify(p))throw new Error("refactor-root-tests-source-stale");const F=at(u,o,d,p);if(h&&ct().sha256!==h.sha256)throw new Error("refactor-root-tests-toolchain-stale");if(x.candidateSha256!==F.candidateSha256)throw new Error("refactor-root-tests-source-stale");let I=l??null;return f==="baseline"&&j.passed&&M&&T&&(I=kt(u,{...i==="vitest"||i==="cpp"?{schemaVersion:2,runner:i,toolchainSha256:S}:{schemaVersion:1},kind:"root-refactor-characterization-baseline",admissionSha256:t,sourceHead:o.sourceHead,testFiles:B,dependencies:P,causalPath:a,candidateSha256:x.candidateSha256,outputSha256:j.outputSha256,causalOutputSha256:T})),Object.freeze({schemaVersion:1,kind:"root-refactor-characterization-observation",phase:f,admissionSha256:t,baselineSha256:I,candidateSha256:x.candidateSha256,outputSha256:j.outputSha256,causalOutputSha256:T,testFileCount:c.length,dependencyCount:d.length,testsPassedObserved:j.passed,causalFailureObserved:M,qaAccepted:!1,gitCommitAuthorized:!1,publicSafe:!0})}catch(o){throw o instanceof Error&&o.message.startsWith("refactor-root-")?o:new Error("refactor-root-tests-unverified")}}function Wt(e){let t="",r="",n,f="",s=!1,a="",l="native",w="plain";const c=[],d=[];try{if(e[0]!=="characterize")throw new Error("refactor-root-tests-arguments-invalid");for(let o=1;o<e.length;o++){const p=e[o],v=()=>{const g=e[++o];if(!g||g.startsWith("--"))throw new Error("refactor-root-tests-arguments-invalid");return g};if(p==="--project-root"&&!t)t=v();else if(p==="--admission-sha"&&!r)r=v();else if(p==="--baseline-sha"&&!n)n=v();else if(p==="--phase"&&!f){const g=v();if(g!=="baseline"&&g!=="after")throw new Error("refactor-root-tests-arguments-invalid");f=g}else if(p==="--test-path")c.push(v());else if(p==="--dependency")d.push(v());else if(p==="--causal-path"&&!a)a=v();else if(p==="--runner"&&l==="native"){const g=v();if(g!=="vitest"&&g!=="cpp")throw new Error("refactor-root-tests-arguments-invalid");l=g}else if(p==="--authorize-run"&&!s)s=!0;else if(p==="--format"&&w==="plain")w=v();else throw new Error("refactor-root-tests-arguments-invalid")}if(!t||!r||!f||!a||!["plain","json"].includes(w))throw new Error("refactor-root-tests-arguments-invalid");const i=xt(t,r,c,d,f,s,a,n,l),u=i.testsPassedObserved&&i.causalFailureObserved;return process.stdout.write(`${JSON.stringify({ok:u,observation:i})}
20
+ `),u?0:1}catch(i){const u=i instanceof Error&&/^refactor-root-[a-z-]+$/.test(i.message)?i.message:"refactor-root-tests-unverified";return process.stdout.write(`${JSON.stringify({ok:!1,reasonCode:u})}
21
+ `),1}}export{Y as readRootRefactorBaseline,Wt as refactorCharacterizationCliHandler,xt as runRootRefactorCharacterization,St as supportsNativeTypeStrippingRuntime,bt as supportsNativeTypeStrippingVersion};