@ornexus/neocortex 4.60.68 → 4.60.69

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