@ornexus/neocortex 4.60.73 → 4.60.74

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 (241) 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.js +1 -108
  22. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
  23. package/packages/client/dist/cache/index.js +1 -13
  24. package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
  25. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
  26. package/packages/client/dist/checkpoint/index.js +1 -11
  27. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
  28. package/packages/client/dist/cli.js +5 -358
  29. package/packages/client/dist/commands/activate.js +8 -394
  30. package/packages/client/dist/commands/architecture-answers.js +7 -274
  31. package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
  32. package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
  33. package/packages/client/dist/commands/architecture-root-binding.js +1 -51
  34. package/packages/client/dist/commands/cache-status.js +2 -112
  35. package/packages/client/dist/commands/config.js +1 -88
  36. package/packages/client/dist/commands/doctor.js +2 -635
  37. package/packages/client/dist/commands/invoke.js +143 -7200
  38. package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
  39. package/packages/client/dist/commands/legacy-story-branch.js +1 -96
  40. package/packages/client/dist/commands/legacy-story-pr.js +1 -276
  41. package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
  42. package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
  43. package/packages/client/dist/commands/mutation-qa-git.js +1 -41
  44. package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
  45. package/packages/client/dist/commands/mutation-qa-run.js +3 -219
  46. package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
  47. package/packages/client/dist/commands/mutation-qa.js +1 -220
  48. package/packages/client/dist/commands/native-agent.js +2 -136
  49. package/packages/client/dist/commands/planning-diagnosis.js +1 -43
  50. package/packages/client/dist/commands/planning-generation.js +1 -53
  51. package/packages/client/dist/commands/planning-memory.js +1 -40
  52. package/packages/client/dist/commands/planning-research.js +1 -42
  53. package/packages/client/dist/commands/private-session.js +1 -75
  54. package/packages/client/dist/commands/property-qa.js +1 -139
  55. package/packages/client/dist/commands/qa-control.js +1 -16
  56. package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
  57. package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
  58. package/packages/client/dist/commands/qa-scan-capture.js +5 -663
  59. package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
  60. package/packages/client/dist/commands/qa-scan-review.js +5 -826
  61. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
  62. package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
  63. package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
  64. package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
  65. package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
  66. package/packages/client/dist/commands/refactor-characterization.js +21 -671
  67. package/packages/client/dist/commands/refactor-commit.js +13 -399
  68. package/packages/client/dist/commands/refactor-control.js +3 -142
  69. package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
  70. package/packages/client/dist/commands/refactor-review.js +5 -283
  71. package/packages/client/dist/commands/refactor-root.js +7 -588
  72. package/packages/client/dist/commands/refresh-memory.js +3 -156
  73. package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
  74. package/packages/client/dist/commands/story-checkpoint.js +1 -171
  75. package/packages/client/dist/config/profile-directory.js +1 -13
  76. package/packages/client/dist/config/resolver-selection.js +1 -278
  77. package/packages/client/dist/config/secure-config.js +12 -281
  78. package/packages/client/dist/config.js +2 -223
  79. package/packages/client/dist/constants.js +1 -25
  80. package/packages/client/dist/context/context-collector.js +2 -222
  81. package/packages/client/dist/context/context-sanitizer.js +1 -145
  82. package/packages/client/dist/continuity/autonomous-governance.js +1 -285
  83. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
  84. package/packages/client/dist/continuity/invoke-hooks.js +1 -129
  85. package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
  86. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
  87. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
  88. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
  89. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
  90. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
  91. package/packages/client/dist/continuity/native-agent-store.js +1 -69
  92. package/packages/client/dist/continuity/native-agent-types.js +1 -90
  93. package/packages/client/dist/continuity/outbox.js +1 -117
  94. package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
  95. package/packages/client/dist/continuity/sqlite-store.js +81 -2612
  96. package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
  97. package/packages/client/dist/errors/error-messages.js +2 -228
  98. package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
  99. package/packages/client/dist/evidence/collector.js +1 -347
  100. package/packages/client/dist/evidence/index.js +1 -1
  101. package/packages/client/dist/evidence/types.js +0 -2
  102. package/packages/client/dist/fs/atomic-state-replace.js +1 -152
  103. package/packages/client/dist/generated/command-registry.generated.js +1 -4076
  104. package/packages/client/dist/generated/public-projections.generated.js +1 -149
  105. package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
  106. package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
  107. package/packages/client/dist/git/epic-operational-state.js +2 -672
  108. package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
  109. package/packages/client/dist/git/git-lifecycle-store.d.ts +23 -0
  110. package/packages/client/dist/git/git-lifecycle-store.js +10 -1981
  111. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
  112. package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
  113. package/packages/client/dist/git/legacy-story-pr.js +70 -3538
  114. package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
  115. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
  116. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
  117. package/packages/client/dist/i18n/first-run.js +2 -60
  118. package/packages/client/dist/index.js +1 -47
  119. package/packages/client/dist/invocation/invoke-input.js +1 -24
  120. package/packages/client/dist/license/index.js +1 -5
  121. package/packages/client/dist/license/license-client.js +1 -304
  122. package/packages/client/dist/machine/fingerprint.js +2 -163
  123. package/packages/client/dist/machine/index.js +1 -5
  124. package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
  125. package/packages/client/dist/memory/project-memory-writer.js +40 -1007
  126. package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
  127. package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -1060
  128. package/packages/client/dist/migrations/project-identity-capability.js +1 -50
  129. package/packages/client/dist/migrations/project-identity.js +7 -493
  130. package/packages/client/dist/migrations/state-json.js +4 -393
  131. package/packages/client/dist/policy/architecture-policy.js +2 -184
  132. package/packages/client/dist/policy/index.js +1 -7
  133. package/packages/client/dist/policy/shared-policy-types.js +0 -38
  134. package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
  135. package/packages/client/dist/private-execution/clean-baseline.js +2 -279
  136. package/packages/client/dist/private-execution/codex-executor.js +4 -574
  137. package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
  138. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
  139. package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
  140. package/packages/client/dist/private-execution/delivery.js +13 -410
  141. package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
  142. package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
  143. package/packages/client/dist/private-execution/implementation-validation.js +2 -106
  144. package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
  145. package/packages/client/dist/private-execution/isolated-session.js +5 -725
  146. package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
  147. package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
  148. package/packages/client/dist/private-execution/pact-test-source.js +1 -258
  149. package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
  150. package/packages/client/dist/private-execution/pact-write-set.js +1 -172
  151. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
  152. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
  153. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
  154. package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
  155. package/packages/client/dist/private-execution/private-report-store.js +1 -42
  156. package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
  157. package/packages/client/dist/private-execution/property-test-choices.js +3 -147
  158. package/packages/client/dist/private-execution/property-test-plan.js +7 -271
  159. package/packages/client/dist/private-execution/property-test-review.js +4 -258
  160. package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
  161. package/packages/client/dist/private-execution/property-test-source.js +1 -139
  162. package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
  163. package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
  164. package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
  165. package/packages/client/dist/private-execution/root-memory-request.js +1 -29
  166. package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
  167. package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
  168. package/packages/client/dist/private-execution/session-lease.js +1 -63
  169. package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
  170. package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
  171. package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
  172. package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
  173. package/packages/client/dist/private-execution/ux-write-set.js +2 -191
  174. package/packages/client/dist/registry/registry-reconciliation.js +1 -234
  175. package/packages/client/dist/resilience/circuit-breaker.js +1 -170
  176. package/packages/client/dist/resilience/degradation-manager.js +1 -164
  177. package/packages/client/dist/resilience/freshness-indicator.js +1 -100
  178. package/packages/client/dist/resilience/index.js +1 -8
  179. package/packages/client/dist/resilience/recovery-detector.js +1 -74
  180. package/packages/client/dist/resolvers/asset-resolver.js +0 -13
  181. package/packages/client/dist/resolvers/local-resolver.js +8 -255
  182. package/packages/client/dist/resolvers/remote-resolver.js +1 -233
  183. package/packages/client/dist/runner/binary.js +1 -404
  184. package/packages/client/dist/runner/cli.js +17 -1571
  185. package/packages/client/dist/runner/headless/adapters.js +1 -49
  186. package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
  187. package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
  188. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
  189. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
  190. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
  191. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
  192. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -1236
  193. package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +18 -2
  194. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
  195. package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
  196. package/packages/client/dist/runner/headless/managed-profile.js +1 -132
  197. package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
  198. package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
  199. package/packages/client/dist/runner/headless/resolver.js +1 -231
  200. package/packages/client/dist/runner/headless/run-yolo.js +1 -885
  201. package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
  202. package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
  203. package/packages/client/dist/runner/headless/types.js +2 -574
  204. package/packages/client/dist/runner/local-console.js +13 -396
  205. package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
  206. package/packages/client/dist/runner/project-identity.js +1 -106
  207. package/packages/client/dist/runner/scheduler.js +7 -995
  208. package/packages/client/dist/runner-cli.js +2 -18
  209. package/packages/client/dist/state/invocation-shape.js +1 -13
  210. package/packages/client/dist/state/project-state-snapshot.js +1 -1731
  211. package/packages/client/dist/state/state-json-repair.js +3 -785
  212. package/packages/client/dist/state/structural-dag-projection.js +1 -118
  213. package/packages/client/dist/telemetry/index.js +1 -5
  214. package/packages/client/dist/telemetry/offline-queue.js +1 -131
  215. package/packages/client/dist/tier/index.js +1 -5
  216. package/packages/client/dist/tier/tier-aware-client.js +1 -405
  217. package/packages/client/dist/types/index.js +1 -38
  218. package/packages/client/dist/version.js +1 -27
  219. package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
  220. package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
  221. package/packages/client/dist/yolo/spec-envelope-collector.js +5 -184
  222. package/packages/client/dist/yoloop/discovery-hook.js +2 -300
  223. package/packages/client/dist/yoloop/index.js +1 -8
  224. package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
  225. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
  226. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
  227. package/packages/client/dist/yoloop/target-normalization.js +1 -180
  228. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1920
  229. package/packages/shared/dist/yolo/change-evidence.js +1 -1
  230. package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
  231. package/targets-stubs/antigravity/gemini.md +1 -1
  232. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  233. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  234. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  235. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  236. package/targets-stubs/claude-code/neocortex.md +2 -2
  237. package/targets-stubs/codex/AGENTS.md +1 -1
  238. package/targets-stubs/cursor/agent.md +2 -2
  239. package/targets-stubs/gemini-cli/agent.md +2 -2
  240. package/targets-stubs/opencode/neocortex-root.md +1 -1
  241. 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};