@planu/cli 5.3.27 → 5.3.28

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 (74) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cli/commands/doctor.d.ts +4 -10
  3. package/dist/cli/commands/doctor.js +5 -31
  4. package/dist/cli/commands/inspect.js +2 -14
  5. package/dist/cli/commands/offline.d.ts +1 -2
  6. package/dist/cli/commands/offline.js +24 -41
  7. package/dist/config/release-policy.json +1 -79
  8. package/dist/engine/auto-updater/release-verifier.js +17 -35
  9. package/dist/engine/core-bridge.d.ts +6 -23
  10. package/dist/engine/core-bridge.js +35 -376
  11. package/dist/engine/execution/job-runtime.d.ts +0 -11
  12. package/dist/engine/execution/job-runtime.js +0 -23
  13. package/dist/engine/living-spec/annotation-parser.js +0 -14
  14. package/dist/engine/project-graph/native.js +2 -2
  15. package/dist/engine/sandbox/profiles/macos.sb +33 -0
  16. package/dist/engine/scan-project/module-discoverer.js +10 -53
  17. package/dist/engine/validation/validation-receipt.d.ts +5 -0
  18. package/dist/engine/validation/validation-receipt.js +9 -1
  19. package/dist/engine/validator/deep-code-checker.js +19 -26
  20. package/dist/security/adapters/macos-keychain.d.ts +5 -3
  21. package/dist/security/adapters/macos-keychain.js +46 -20
  22. package/dist/tools/create-spec/autopilot-analyzer.d.ts +1 -1
  23. package/dist/tools/create-spec/autopilot-analyzer.js +2 -2
  24. package/dist/tools/create-spec/relevance-scorer.d.ts +1 -1
  25. package/dist/tools/create-spec/relevance-scorer.js +1 -17
  26. package/dist/tools/create-spec.js +1 -1
  27. package/dist/types/atomic-upgrade.d.ts +0 -2
  28. package/dist/types/core-bridge.d.ts +0 -108
  29. package/dist/types/core-bridge.js +1 -1
  30. package/dist/types/durable-job.d.ts +0 -40
  31. package/dist/types/security.d.ts +0 -5
  32. package/package.json +7 -43
  33. package/planu-plugin.json +1 -1
  34. package/dist/config/native-release-public-key.pem +0 -3
  35. package/dist/config/native-targets.json +0 -87
  36. package/dist/engine/execution/job-executor.d.ts +0 -16
  37. package/dist/engine/execution/job-executor.js +0 -140
  38. package/dist/engine/execution/native-relevance-scorer.d.ts +0 -6
  39. package/dist/engine/execution/native-relevance-scorer.js +0 -64
  40. package/dist/engine/execution/native-worker-dispatch.d.ts +0 -3
  41. package/dist/engine/execution/native-worker-dispatch.js +0 -46
  42. package/dist/engine/execution/native-worker-protocol.d.ts +0 -6
  43. package/dist/engine/execution/native-worker-protocol.js +0 -33
  44. package/dist/engine/execution/native-worker-runtime.d.ts +0 -4
  45. package/dist/engine/execution/native-worker-runtime.js +0 -177
  46. package/dist/engine/execution/native-worker-thread.d.ts +0 -2
  47. package/dist/engine/execution/native-worker-thread.js +0 -66
  48. package/dist/engine/macos-keychain-native-port.d.ts +0 -10
  49. package/dist/engine/macos-keychain-native-port.js +0 -64
  50. package/dist/engine/native-capability-policy.d.ts +0 -4
  51. package/dist/engine/native-capability-policy.js +0 -24
  52. package/dist/engine/native-platform.d.ts +0 -3
  53. package/dist/engine/native-platform.js +0 -44
  54. package/dist/engine/native-runtime-handshake.d.ts +0 -20
  55. package/dist/engine/native-runtime-handshake.js +0 -230
  56. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +0 -39
  57. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +0 -1310
  58. package/dist/engine/planu-core.darwin-x64.node.manifest.json +0 -39
  59. package/dist/engine/planu-core.darwin-x64.node.sbom.json +0 -1310
  60. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +0 -38
  61. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +0 -1310
  62. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +0 -38
  63. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +0 -1310
  64. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +0 -38
  65. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +0 -1310
  66. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +0 -38
  67. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +0 -1310
  68. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +0 -38
  69. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +0 -1310
  70. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +0 -38
  71. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +0 -1310
  72. package/dist/types/native-worker.d.ts +0 -48
  73. package/dist/types/native-worker.js +0 -2
  74. package/planu-native.json +0 -82
@@ -1,20 +1,12 @@
1
- import { createRequire } from 'node:module';
2
- import { isMainThread } from 'node:worker_threads';
3
1
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
4
2
  import { join, extname, basename } from 'node:path';
5
3
  import { createHash, createHmac } from 'node:crypto';
6
4
  import { tsExtractTsRelations, tsQueryAffectedNodes, tsQueryShortestPath, resolveProjectGraphLimits, tsScanProjectGraphSources, tsSelectCompactGraphSlice, } from './core-bridge-project-graph.js';
7
- import { PLANU_VERSION } from '../config/version.js';
8
- import { negotiateNativeRuntime, inspectNativeArtifact, NATIVE_CAPABILITY_ABI, NATIVE_NODE_API_ABI, NATIVE_PROTOCOL_ABI, RUNTIME_STORAGE_SCHEMA, } from './native-runtime-handshake.js';
9
- import { createMacOSKeychainNativePort } from './macos-keychain-native-port.js';
10
- import { NATIVE_CAPABILITY_POLICIES } from './native-capability-policy.js';
11
- import { detectMusl } from './native-platform.js';
12
5
  import { findDuplicateBlocks } from './core-bridge-duplicate-blocks.js';
13
6
  import { appendGapEntry, verifyGapsChain } from './core-bridge-gaps.js';
14
7
  import { reportClassifiedDegradation } from '../errors/classified-degradation.js';
15
- import { runNativeWorkerOperation } from './execution/native-worker-runtime.js';
16
- export { negotiateNativeRuntime, NATIVE_CAPABILITY_ABI, NATIVE_NODE_API_ABI, NATIVE_PROTOCOL_ABI, RUNTIME_STORAGE_SCHEMA, };
17
- const require = createRequire(import.meta.url);
8
+ /** Must match RuntimeDatabase.schemaVersion and the packaged runtime's release contract. */
9
+ export const RUNTIME_STORAGE_SCHEMA = 4;
18
10
  const reportedDegradations = new Set();
19
11
  const FALLBACK_ERROR = new Error('Native fallback operation degraded');
20
12
  function reportOnce(code, report) {
@@ -25,209 +17,6 @@ function reportOnce(code, report) {
25
17
  }
26
18
  class InvalidFallbackPatternError extends Error {
27
19
  }
28
- function getPlatformInfo() {
29
- return {
30
- platform: process.platform,
31
- arch: process.arch,
32
- libc: process.platform === 'linux' ? (detectMusl() ? 'musl' : 'glibc') : null,
33
- };
34
- }
35
- function platformPackageName(info) {
36
- const { platform, arch, libc } = info;
37
- if (platform === 'darwin') {
38
- if (arch === 'arm64') {
39
- return '@planu/core-darwin-arm64';
40
- }
41
- if (arch === 'x64') {
42
- return '@planu/core-darwin-x64';
43
- }
44
- }
45
- if (platform === 'linux') {
46
- if (arch === 'x64') {
47
- return libc === 'musl' ? '@planu/core-linux-x64-musl' : '@planu/core-linux-x64-gnu';
48
- }
49
- if (arch === 'arm64') {
50
- return libc === 'musl' ? '@planu/core-linux-arm64-musl' : '@planu/core-linux-arm64-gnu';
51
- }
52
- }
53
- if (platform === 'win32') {
54
- if (arch === 'x64') {
55
- return '@planu/core-win32-x64-msvc';
56
- }
57
- if (arch === 'arm64') {
58
- return '@planu/core-win32-arm64-msvc';
59
- }
60
- }
61
- return null;
62
- }
63
- export function currentNativePackageName() {
64
- return platformPackageName(getPlatformInfo());
65
- }
66
- function localBinaryName(info) {
67
- const { platform, arch, libc } = info;
68
- if (platform === 'linux') {
69
- const suffix = libc === 'musl' ? 'musl' : 'gnu';
70
- return `planu-core.linux-${arch}-${suffix}.node`;
71
- }
72
- if (platform === 'win32') {
73
- return `planu-core.win32-${arch}-msvc.node`;
74
- }
75
- return `planu-core.${platform}-${arch}.node`;
76
- }
77
- // Native loader --------------------------------------------------------------
78
- function nativeTarget(info) {
79
- return [info.platform, info.arch, info.libc].filter(Boolean).join('-');
80
- }
81
- let nativeModule = null;
82
- let loadDiagnostic = { attempted: [], errors: [] };
83
- let loaded = false;
84
- function loadNative() {
85
- if (process.env.DISABLE_NATIVE_CORE === '1') {
86
- loadDiagnostic = { attempted: [], errors: [] };
87
- loaded = true;
88
- return null;
89
- }
90
- if (loaded) {
91
- return nativeModule;
92
- }
93
- loaded = true;
94
- const rejectedOverrides = ['PLANU_NATIVE_PATH', 'NAPI_RS_NATIVE_LIBRARY_PATH'].filter((name) => process.env[name] !== undefined);
95
- if (rejectedOverrides.length > 0) {
96
- loadDiagnostic = {
97
- attempted: [],
98
- errors: ['Untrusted native path overrides are forbidden'],
99
- rejectedOverrides,
100
- };
101
- return null;
102
- }
103
- const info = getPlatformInfo();
104
- const attempts = [];
105
- const errors = [];
106
- const target = nativeTarget(info);
107
- const tryRequire = (originLabel, id, artifactPath) => {
108
- attempts.push(originLabel);
109
- try {
110
- const resolvedPath = artifactPath ?? require.resolve(id);
111
- const { artifactSha256, manifest } = inspectNativeArtifact(resolvedPath);
112
- if (!manifest) {
113
- errors.push(`${originLabel}: native artifact manifest is missing, unsafe or invalid`);
114
- return null;
115
- }
116
- const candidate = require(resolvedPath);
117
- let handshake = null;
118
- try {
119
- handshake = candidate.planuRuntimeHandshake?.() ?? null;
120
- }
121
- catch (error) {
122
- void error;
123
- /* reliability-optional: NATIVE_HANDSHAKE_FAILED */
124
- reportOnce('NATIVE_HANDSHAKE_FAILED', () => {
125
- reportClassifiedDegradation('NATIVE_HANDSHAKE_FAILED', FALLBACK_ERROR);
126
- });
127
- errors.push(`${originLabel}: native handshake failed`);
128
- return null;
129
- }
130
- const compatibility = negotiateNativeRuntime(handshake, manifest, {
131
- cliVersion: PLANU_VERSION,
132
- target,
133
- artifactSha256,
134
- });
135
- if (!compatibility.compatible) {
136
- errors.push(`${originLabel}: incompatible native runtime`);
137
- loadDiagnostic = { attempted: attempts, errors, compatibility };
138
- return null;
139
- }
140
- loadDiagnostic = { attempted: attempts, errors, compatibility };
141
- loadDiagnostic.artifactSha256 = artifactSha256;
142
- return candidate;
143
- }
144
- catch (error) {
145
- void error;
146
- /* reliability-optional: NATIVE_ARTIFACT_LOAD_FAILED */
147
- reportOnce('NATIVE_ARTIFACT_LOAD_FAILED', () => {
148
- reportClassifiedDegradation('NATIVE_ARTIFACT_LOAD_FAILED', FALLBACK_ERROR);
149
- });
150
- errors.push(`${originLabel}: native artifact load failed`);
151
- return null;
152
- }
153
- };
154
- // 1) Local platform-tagged binary in the same dir (dev builds + bundled tarball).
155
- const localName = localBinaryName(info);
156
- const localPath = join(import.meta.dirname, localName);
157
- if (existsSync(localPath)) {
158
- const mod = tryRequire(`local:${localName}`, localPath, localPath);
159
- if (mod) {
160
- loadDiagnostic = {
161
- ...loadDiagnostic,
162
- attempted: attempts,
163
- errors,
164
- loaded: localName,
165
- origin: 'local',
166
- };
167
- nativeModule = mod;
168
- console.error(`[Planu-RS] Verified native core loaded (${localName}).`);
169
- return mod;
170
- }
171
- }
172
- // 2) Optional dependency subpackage installed by npm. Untagged legacy
173
- // binaries are intentionally rejected: they cannot prove target/ABI/schema.
174
- const pkg = platformPackageName(info);
175
- if (pkg) {
176
- const mod = tryRequire(`package:${pkg}`, pkg);
177
- if (mod) {
178
- loadDiagnostic = {
179
- ...loadDiagnostic,
180
- attempted: attempts,
181
- errors,
182
- loaded: pkg,
183
- origin: 'package',
184
- };
185
- nativeModule = mod;
186
- console.error(`[Planu-RS] Verified native core loaded (${pkg}).`);
187
- return mod;
188
- }
189
- }
190
- loadDiagnostic = { ...loadDiagnostic, attempted: attempts, errors };
191
- return null;
192
- }
193
- function refreshNativeCapabilityDiagnostic() {
194
- const rejectedOverrides = ['PLANU_NATIVE_PATH', 'NAPI_RS_NATIVE_LIBRARY_PATH'].filter((name) => process.env[name] !== undefined);
195
- if (!loaded && rejectedOverrides.length > 0) {
196
- loadDiagnostic = {
197
- attempted: [],
198
- errors: ['Untrusted native path overrides are forbidden'],
199
- rejectedOverrides,
200
- };
201
- }
202
- const runtimeCapabilities = loadDiagnostic.compatibility?.compatible === true
203
- ? loadDiagnostic.compatibility.handshake?.capabilities
204
- : undefined;
205
- loadDiagnostic.capabilities = NATIVE_CAPABILITY_POLICIES.map((policy) => ({
206
- capability: policy.capability,
207
- state: runtimeCapabilities?.includes(policy.capability)
208
- ? 'available'
209
- : policy.fallback === 'fail-closed'
210
- ? 'unavailable'
211
- : 'degraded',
212
- fallback: policy.fallback,
213
- }));
214
- loadDiagnostic.executionScope = 'isolated-worker-and-keychain-port';
215
- }
216
- refreshNativeCapabilityDiagnostic();
217
- // Synchronous bindings are deliberately confined to the durable native worker.
218
- // Main-thread callers use parity-tested TypeScript paths and can observe that
219
- // degradation through nativeLoadDiagnostic()/`planu inspect`.
220
- const getNative = () => process.env.DISABLE_NATIVE_CORE === '1' || isMainThread ? null : loadNative();
221
- /** Main-thread port for async Keychain calls; never accepts override or unverified native code. */
222
- export function requireMacOSKeychainNativePort() {
223
- const keychainNative = loadNative();
224
- refreshNativeCapabilityDiagnostic();
225
- return createMacOSKeychainNativePort(process.platform, process.env.PLANU_NATIVE_PATH !== undefined ||
226
- process.env.NAPI_RS_NATIVE_LIBRARY_PATH !== undefined, process.env.DISABLE_NATIVE_CORE === '1', keychainNative, loadDiagnostic.origin, loadDiagnostic.compatibility?.handshake?.capabilities, loadDiagnostic.compatibility);
227
- }
228
- // TypeScript fallbacks -------------------------------------------------------
229
- // These keep the surface area working when the native binary is unavailable
230
- // (unsupported platform, sandboxed install, dev environment without build).
231
20
  const PROJECT_ID_RE = /^[a-f0-9]{16,64}$/;
232
21
  const SPEC_ANNOTATION_RE = /@spec\s+(SPEC-\d+)(?:\s+AC:([\d,]+))?(?:\s+(.*))?/;
233
22
  const SCANNABLE_EXTS = new Set(['.ts', '.js', '.tsx', '.jsx', '.rs', '.py', '.go']);
@@ -427,8 +216,6 @@ function tsHnswSearchFlat(query, nodes, topK) {
427
216
  .slice(0, topK);
428
217
  }
429
218
  function tsHmacSign(secret, data) {
430
- // Must produce the same digest as the Rust `fastHmacSign` (HMAC-SHA256, not
431
- // a plain SHA-256 of `secret + data`) so signatures roundtrip across paths.
432
219
  return createHmac('sha256', secret).update(data).digest('hex');
433
220
  }
434
221
  function tsHmacVerify(secret, data, signature) {
@@ -500,39 +287,27 @@ function tsGetProjectDataPath(baseDir, projectId) {
500
287
  }
501
288
  // Public API -----------------------------------------------------------------
502
289
  export function fastHashProjectPath(projectPath) {
503
- const n = getNative();
504
- return n ? n.hashProjectPath(projectPath) : tsHashProjectPath(projectPath);
290
+ return tsHashProjectPath(projectPath);
505
291
  }
506
292
  export function fastGetProjectDataPath(baseDir, projectId) {
507
- const n = getNative();
508
- return n ? n.getProjectDataPath(baseDir, projectId) : tsGetProjectDataPath(baseDir, projectId);
293
+ return tsGetProjectDataPath(baseDir, projectId);
509
294
  }
510
295
  export function fastIsSafeProjectId(projectId) {
511
- const n = getNative();
512
- return n ? n.isSafeProjectId(projectId) : PROJECT_ID_RE.test(projectId);
296
+ return PROJECT_ID_RE.test(projectId);
513
297
  }
514
298
  export function fastScanAndHashFiles(rootPath) {
515
- const n = getNative();
516
- return n ? n.scanAndHashFiles(rootPath) : tsScanAndHashFiles(rootPath);
299
+ return tsScanAndHashFiles(rootPath);
517
300
  }
518
301
  export function fastScanProjectMetadata(rootPath) {
519
- const n = getNative();
520
- return n ? n.scanProjectMetadata(rootPath) : tsScanProjectMetadata(rootPath);
302
+ return tsScanProjectMetadata(rootPath);
521
303
  }
522
304
  export function fastScanSpecAnnotations(rootPath) {
523
- const n = getNative();
524
- return n ? n.scanSpecAnnotations(rootPath) : tsScanSpecAnnotations(rootPath);
305
+ return tsScanSpecAnnotations(rootPath);
525
306
  }
526
307
  export function fastCheckAcCoverage(filePaths, keywords) {
527
- const n = getNative();
528
- return n ? n.checkAcCoverage(filePaths, keywords) : tsCheckAcCoverage(filePaths, keywords);
308
+ return tsCheckAcCoverage(filePaths, keywords);
529
309
  }
530
310
  export function fastDetectDriftParallel(rootPath, keywords) {
531
- const n = getNative();
532
- if (n) {
533
- return n.fastDetectDriftParallel(rootPath, keywords);
534
- }
535
- // Fallback: simple sequential scan.
536
311
  const files = Array.from(walkFiles(rootPath));
537
312
  const lowered = keywords.map((k) => k.toLowerCase());
538
313
  const found = new Set();
@@ -562,192 +337,76 @@ export function fastDetectDriftParallel(rootPath, keywords) {
562
337
  return { foundKeywords: Array.from(found), fileCount: files.length };
563
338
  }
564
339
  export function fastFindDuplicateBlocks(paths, minLines) {
565
- const n = getNative();
566
- if (n) {
567
- return n.fastFindDuplicateBlocks(paths, minLines);
568
- }
569
340
  return findDuplicateBlocks(paths, minLines);
570
341
  }
571
342
  export function fastFindPatterns(rootPath, patterns, extensions = [], exclude = []) {
572
- const n = getNative();
573
- return n
574
- ? n.fastFindPatterns(rootPath, patterns, extensions, exclude)
575
- : tsFindPatterns(rootPath, patterns, extensions, exclude);
343
+ return tsFindPatterns(rootPath, patterns, extensions, exclude);
576
344
  }
577
345
  export function fastFindFilesByName(rootPath, names) {
578
- const n = getNative();
579
- return n ? n.fastFindFilesByName(rootPath, names) : tsFindFilesByName(rootPath, names);
346
+ return tsFindFilesByName(rootPath, names);
580
347
  }
581
348
  export function fastFindFilesByExt(rootPath, extensions) {
582
- const n = getNative();
583
- return n ? n.fastFindFilesByExt(rootPath, extensions) : tsFindFilesByExt(rootPath, extensions);
349
+ return tsFindFilesByExt(rootPath, extensions);
584
350
  }
585
351
  export function fastReadFiles(paths) {
586
- const n = getNative();
587
- return n ? n.fastReadFiles(paths) : tsReadFiles(paths);
352
+ return tsReadFiles(paths);
588
353
  }
589
- export function fastScanSpecs(rootPath) {
590
- const n = getNative();
591
- if (n) {
592
- return n.fastScanSpecs(rootPath);
593
- }
594
- // No TS fallback yet — callers should detect and use the regular spec
595
- // scanner instead. Returning null preserves the existing semantics.
354
+ /** No TS-only replacement exists yet — callers detect `null` and use the regular spec scanner. */
355
+ export function fastScanSpecs(_rootPath) {
596
356
  return null;
597
357
  }
598
358
  export function fastVerifyGapsChain(filePath) {
599
- const n = getNative();
600
- if (n) {
601
- return n.fastVerifyGapsChain(filePath);
602
- }
603
359
  return verifyGapsChain(filePath);
604
360
  }
605
361
  export function fastAppendGapEntry(filePath, id, timestamp, specId, description, severity, affectedSpecs) {
606
- const n = getNative();
607
- if (n) {
608
- return n.fastAppendGapEntry(filePath, id, timestamp, specId, description, severity, affectedSpecs);
609
- }
610
362
  return appendGapEntry(filePath, id, timestamp, specId, description, severity, affectedSpecs);
611
363
  }
612
364
  export function fastCosineSimilarity(v1, v2) {
613
- const n = getNative();
614
- return n ? n.fastCosineSimilarity(v1, v2) : tsCosineSimilarity(v1, v2);
365
+ return tsCosineSimilarity(v1, v2);
615
366
  }
616
367
  export function fastHnswSearchFlat(query, nodes, topK) {
617
- const n = getNative();
618
- return n ? n.fastHnswSearchFlat(query, nodes, topK) : tsHnswSearchFlat(query, nodes, topK);
368
+ return tsHnswSearchFlat(query, nodes, topK);
619
369
  }
620
370
  export function fastHmacSign(secret, data) {
621
- const n = getNative();
622
- return n ? n.fastHmacSign(secret, data) : tsHmacSign(secret, data);
371
+ return tsHmacSign(secret, data);
623
372
  }
624
373
  export function fastHmacVerify(secret, data, signature) {
625
- const n = getNative();
626
- return n ? n.fastHmacVerify(secret, data, signature) : tsHmacVerify(secret, data, signature);
374
+ return tsHmacVerify(secret, data, signature);
627
375
  }
628
376
  export function fastScanProjectGraphSources(rootPath, policy, overrides = {}) {
629
377
  const limits = resolveProjectGraphLimits(policy, overrides);
630
- const n = getNative();
631
- if (typeof n?.scanProjectGraphSources === 'function') {
632
- return n.scanProjectGraphSources(rootPath, limits);
633
- }
634
378
  return tsScanProjectGraphSources(rootPath, limits);
635
379
  }
636
380
  export function fastExtractTsRelations(rootPath, paths, policy, overrides = {}) {
637
381
  const limits = resolveProjectGraphLimits(policy, overrides);
638
- const n = getNative();
639
- if (typeof n?.extractTsRelations === 'function') {
640
- return n.extractTsRelations(rootPath, paths, limits);
641
- }
642
382
  return tsExtractTsRelations(rootPath, paths, limits);
643
383
  }
644
- function compactEdges(edges) {
645
- return {
646
- ids: edges.map((edge) => edge.id),
647
- from: edges.map((edge) => edge.from),
648
- to: edges.map((edge) => edge.to),
649
- relations: edges.map((edge) => edge.relation),
650
- };
651
- }
652
384
  export function fastQueryAffectedNodes(seed, edges, relationFilter, policy, overrides = {}) {
653
385
  const limits = resolveProjectGraphLimits(policy, overrides);
654
- const n = getNative();
655
- if (typeof n?.queryAffectedNodesCompact === 'function') {
656
- return n.queryAffectedNodesCompact(seed, compactEdges(edges), relationFilter, limits);
657
- }
658
- if (typeof n?.queryAffectedNodes === 'function') {
659
- return n.queryAffectedNodes(seed, edges, relationFilter, limits);
660
- }
661
386
  return tsQueryAffectedNodes(seed, edges, relationFilter, limits);
662
387
  }
663
388
  export function fastQueryShortestPath(from, to, edges, relationFilter, policy, overrides = {}) {
664
389
  const limits = resolveProjectGraphLimits(policy, overrides);
665
- const n = getNative();
666
- if (typeof n?.queryShortestPath === 'function') {
667
- return n.queryShortestPath(from, to, edges, relationFilter, limits);
668
- }
669
390
  return tsQueryShortestPath(from, to, edges, relationFilter, limits);
670
391
  }
671
392
  export function fastSelectCompactGraphSlice(seeds, nodes, edges, policy, overrides = {}) {
672
393
  const limits = resolveProjectGraphLimits(policy, overrides);
673
- const n = getNative();
674
- if (typeof n?.selectCompactGraphSliceCompact === 'function') {
675
- return n.selectCompactGraphSliceCompact(seeds, nodes.map((node) => node.id), compactEdges(edges), limits);
676
- }
677
- if (typeof n?.selectCompactGraphSlice === 'function') {
678
- return n.selectCompactGraphSlice(seeds, nodes, edges, limits);
679
- }
680
394
  return tsSelectCompactGraphSlice(seeds, nodes, edges, limits);
681
395
  }
682
- // Main-thread dispatch strategy (SPEC-1344) -----------------------------------
683
- // getNative() deliberately returns null on the main thread so long native FFI
684
- // calls never block the event loop (see comment above getNative()). For hot
685
- // paths that need acceleration anyway, dispatchViaWorker() moves the call onto
686
- // the existing native-worker-dispatch.ts runtime (a worker thread, where
687
- // isMainThread is false and native loads normally). If the worker fails to
688
- // spawn, times out, or the operation isn't allowlisted, it falls back to the
689
- // caller-supplied synchronous implementation so behavior never regresses.
690
- const WORKER_DISPATCH_TIMEOUT_MS = 30_000;
691
- const DEGRADATION_NOTICE_THRESHOLD_MS = 2_000;
692
- async function dispatchViaWorker(operation, payload, projectId, syncFallback) {
693
- const start = Date.now();
694
- if (isMainThread && process.env.DISABLE_NATIVE_CORE !== '1') {
695
- try {
696
- const value = (await runNativeWorkerOperation({
697
- operation,
698
- payload,
699
- projectId,
700
- deadlineAt: start + WORKER_DISPATCH_TIMEOUT_MS,
701
- }));
702
- return { value };
703
- }
704
- catch (error) {
705
- /* reliability-optional: NATIVE_WORKER_DISPATCH_FAILED — falls through to sync fallback */
706
- reportOnce(`NATIVE_WORKER_DISPATCH_FAILED:${operation}`, () => {
707
- reportClassifiedDegradation('NATIVE_WORKER_DISPATCH_FAILED', error instanceof Error ? error : FALLBACK_ERROR);
708
- });
709
- }
710
- }
711
- const value = syncFallback();
712
- const elapsedMs = Date.now() - start;
713
- if (elapsedMs > DEGRADATION_NOTICE_THRESHOLD_MS) {
714
- return {
715
- value,
716
- degradationNotice: `native engine unavailable — run planu doctor (fallback took ${String(elapsedMs)}ms)`,
717
- };
718
- }
719
- return { value };
720
- }
721
- /** Worker-dispatched variant of {@link fastDetectDriftParallel} for main-thread callers. */
722
- export async function fastDetectDriftParallelAsync(rootPath, keywords) {
723
- return dispatchViaWorker('detect-drift', { rootPath, keywords }, fastHashProjectPath(rootPath), () => fastDetectDriftParallel(rootPath, keywords));
724
- }
725
- /** Worker-dispatched variant of {@link fastScanAndHashFiles} for main-thread callers. */
726
- export async function fastScanAndHashFilesAsync(rootPath) {
727
- return dispatchViaWorker('scan-and-hash-files', { rootPath }, fastHashProjectPath(rootPath), () => fastScanAndHashFiles(rootPath));
728
- }
729
- /** Worker-dispatched variant of {@link fastScanProjectMetadata} for main-thread callers. */
730
- export async function fastScanProjectMetadataAsync(rootPath) {
731
- return dispatchViaWorker('scan-project-metadata', { rootPath }, fastHashProjectPath(rootPath), () => fastScanProjectMetadata(rootPath));
732
- }
733
- /** Worker-dispatched variant of {@link fastScanSpecs} for main-thread callers. */
734
- export async function fastScanSpecsAsync(rootPath) {
735
- return dispatchViaWorker('scan-specs', { rootPath }, fastHashProjectPath(rootPath), () => fastScanSpecs(rootPath));
736
- }
737
- export const isNativeActive = () => getNative() !== null;
738
- export const nativeLoadDiagnostic = () => {
739
- refreshNativeCapabilityDiagnostic();
740
- return loadDiagnostic;
741
- };
742
- /**
743
- * Force a native load attempt regardless of the calling thread and return the
744
- * resulting diagnostic. `nativeLoadDiagnostic()` alone only reports load state
745
- * that some other main-thread-bypassing caller already triggered (e.g. the
746
- * macOS Keychain port); this is the main-thread-safe way to actually probe
747
- * native availability, used by `planu doctor`.
748
- */
749
- export const probeNativeLoad = () => {
750
- loadNative();
751
- return nativeLoadDiagnostic();
752
- };
396
+ // Async wrappers (SPEC-1344) --------------------------------------------------
397
+ // These used to dispatch onto a native worker thread for hot paths. With the
398
+ // native runtime removed, they call the synchronous TS implementation
399
+ // directly; the shape stays a Promise so existing callers are unaffected.
400
+ export function fastDetectDriftParallelAsync(rootPath, keywords) {
401
+ return Promise.resolve({ value: fastDetectDriftParallel(rootPath, keywords) });
402
+ }
403
+ export function fastScanAndHashFilesAsync(rootPath) {
404
+ return Promise.resolve({ value: fastScanAndHashFiles(rootPath) });
405
+ }
406
+ export function fastScanProjectMetadataAsync(rootPath) {
407
+ return Promise.resolve({ value: fastScanProjectMetadata(rootPath) });
408
+ }
409
+ export function fastScanSpecsAsync(rootPath) {
410
+ return Promise.resolve({ value: fastScanSpecs(rootPath) });
411
+ }
753
412
  //# sourceMappingURL=core-bridge.js.map
@@ -1,6 +1,5 @@
1
1
  import { type DurableJobRecord } from '../../storage/runtime-db.js';
2
2
  import type { DurableJobIdentity, DurableJobRuntimeOptions, WatchJobInput } from '../../types/durable-job.js';
3
- import type { NativeWorkerOperation } from '../../types/native-worker.js';
4
3
  export declare class DurableJobNotFoundError extends Error {
5
4
  constructor(operationId: string);
6
5
  }
@@ -18,16 +17,6 @@ export declare class DurableJobRuntime implements Disposable {
18
17
  readonly payload: unknown;
19
18
  readonly maxAttempts?: number;
20
19
  }): DurableJobRecord;
21
- acceptNative(input: {
22
- readonly projectId: string;
23
- readonly workspaceId?: string;
24
- readonly operationId?: string;
25
- readonly operation: NativeWorkerOperation;
26
- readonly payload: Readonly<Record<string, unknown>>;
27
- readonly deadlineAt: number;
28
- readonly cancellationId?: string;
29
- readonly maxAttempts?: number;
30
- }): DurableJobRecord;
31
20
  get(input: DurableJobIdentity): DurableJobRecord;
32
21
  /** Return the allowlisted public projection for one job and its current queue scope. */
33
22
  observe(input: DurableJobIdentity | DurableJobRecord): ReturnType<typeof publicJobObservation>;
@@ -2,7 +2,6 @@ import { createHash, randomUUID } from 'node:crypto';
2
2
  import { RuntimeDatabase, } from '../../storage/runtime-db.js';
3
3
  import { resolveStorageLayout } from '../../storage/storage-layout.js';
4
4
  import { getRuntimePolicy } from '../runtime-policy.js';
5
- import { isNativeWorkerOperation } from './native-worker-protocol.js';
6
5
  import { getExecutionContext } from './context.js';
7
6
  import { sanitizePublicJobValue } from './public-job-sanitizer.js';
8
7
  const DEFAULT_WORKSPACE_ID = 'global';
@@ -42,28 +41,6 @@ export class DurableJobRuntime {
42
41
  });
43
42
  return this.requireJob({ operationId, projectId: input.projectId, workspaceId });
44
43
  }
45
- acceptNative(input) {
46
- if (!isNativeWorkerOperation(input.operation)) {
47
- throw new Error('[Planu] Native worker operation is not allowlisted');
48
- }
49
- if (!Number.isSafeInteger(input.deadlineAt) || input.deadlineAt <= Date.now()) {
50
- throw new Error('[Planu] Native durable job requires a future absolute deadline');
51
- }
52
- const payload = {
53
- kind: 'native-worker',
54
- operation: input.operation,
55
- payload: input.payload,
56
- deadlineAt: input.deadlineAt,
57
- ...(input.cancellationId === undefined ? {} : { cancellationId: input.cancellationId }),
58
- };
59
- return this.accept({
60
- projectId: input.projectId,
61
- workspaceId: input.workspaceId,
62
- operationId: input.operationId,
63
- payload,
64
- maxAttempts: input.maxAttempts,
65
- });
66
- }
67
44
  get(input) {
68
45
  return this.requireJob(input);
69
46
  }
@@ -68,24 +68,10 @@ export function parseAnnotationsFromContent(content, file) {
68
68
  }
69
69
  return annotations;
70
70
  }
71
- import { fastScanSpecAnnotations, isNativeActive } from '../core-bridge.js';
72
71
  /**
73
72
  * Parse all @spec annotations from a project directory.
74
73
  */
75
74
  export async function parseAnnotationsFromProject(projectPath, filterSpecId) {
76
- if (isNativeActive()) {
77
- const allAnnotations = fastScanSpecAnnotations(projectPath);
78
- const annotations = filterSpecId
79
- ? allAnnotations.filter((a) => a.specId === filterSpecId)
80
- : allAnnotations;
81
- // Estimate file counts (approximate for performance if native)
82
- const annotatedFilePaths = new Set(allAnnotations.map((a) => a.file));
83
- return {
84
- annotations,
85
- fileCount: 0, // In native mode, we don't count files unless requested
86
- annotatedFileCount: annotatedFilePaths.size,
87
- };
88
- }
89
75
  const annotations = [];
90
76
  let fileCount = 0;
91
77
  let annotatedFileCount = 0;
@@ -1,4 +1,4 @@
1
- import { fastExtractTsRelations, fastQueryAffectedNodes, fastQueryShortestPath, fastScanProjectGraphSources, fastSelectCompactGraphSlice, isNativeActive, } from '../core-bridge.js';
1
+ import { fastExtractTsRelations, fastQueryAffectedNodes, fastQueryShortestPath, fastScanProjectGraphSources, fastSelectCompactGraphSlice, } from '../core-bridge.js';
2
2
  function toNativeNodes(nodes) {
3
3
  return nodes.map((node) => ({ id: node.id }));
4
4
  }
@@ -12,7 +12,7 @@ function toNativeEdges(edges) {
12
12
  }
13
13
  export function createNativeProjectGraphRuntime(limitsPolicy) {
14
14
  return {
15
- nativeActive: isNativeActive(),
15
+ nativeActive: false,
16
16
  scanSources(projectPath, maxFiles) {
17
17
  return fastScanProjectGraphSources(projectPath, limitsPolicy, maxFiles === undefined ? {} : { maxFiles });
18
18
  },
@@ -0,0 +1,33 @@
1
+ ;; macos.sb — sandbox-exec profile for Planu execute_sdd_flow (SPEC-742)
2
+ ;; TinyScheme syntax (Apple sandbox profile language).
3
+ ;; Denies all by default, then allows specific operations.
4
+
5
+ (version 1)
6
+
7
+ ;; Deny everything by default
8
+ (deny default)
9
+
10
+ ;; Allow read-only filesystem access (system libraries, binaries)
11
+ (allow file-read*)
12
+
13
+ ;; Allow write access to project root (parameterized) and /tmp
14
+ ;; Note: The profile is parameterized with PROJECT_ROOT at runtime.
15
+ (allow file-write*
16
+ (subpath (param "PROJECT_ROOT"))
17
+ (subpath "/tmp")
18
+ (subpath "/private/tmp"))
19
+
20
+ ;; Allow process execution (for spawning build tools)
21
+ (allow process-exec)
22
+ (allow process-fork)
23
+
24
+ ;; Allow network for npm registry only (optional — controlled by network param)
25
+ ;; Blocked by default — uncomment to allow specific connections
26
+ ;; (allow network-outbound (remote ip "104.16.0.0/12"))
27
+
28
+ ;; Allow IPC for process communication
29
+ (allow ipc-posix-shm)
30
+ (allow mach-lookup)
31
+
32
+ ;; Allow reading system information
33
+ (allow sysctl-read)