@nforma.ai/nforma 0.2.1 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/README.md +2 -2
  2. package/agents/{qgsd-codebase-mapper.md → nf-codebase-mapper.md} +1 -1
  3. package/agents/{qgsd-debugger.md → nf-debugger.md} +3 -3
  4. package/agents/{qgsd-executor.md → nf-executor.md} +14 -14
  5. package/agents/{qgsd-integration-checker.md → nf-integration-checker.md} +1 -1
  6. package/agents/{qgsd-phase-researcher.md → nf-phase-researcher.md} +6 -6
  7. package/agents/{qgsd-plan-checker.md → nf-plan-checker.md} +9 -9
  8. package/agents/{qgsd-planner.md → nf-planner.md} +9 -9
  9. package/agents/{qgsd-project-researcher.md → nf-project-researcher.md} +2 -2
  10. package/agents/{qgsd-quorum-orchestrator.md → nf-quorum-orchestrator.md} +33 -33
  11. package/agents/{qgsd-quorum-slot-worker.md → nf-quorum-slot-worker.md} +3 -3
  12. package/agents/{qgsd-quorum-synthesizer.md → nf-quorum-synthesizer.md} +3 -3
  13. package/agents/{qgsd-quorum-test-worker.md → nf-quorum-test-worker.md} +1 -1
  14. package/agents/{qgsd-quorum-worker.md → nf-quorum-worker.md} +6 -6
  15. package/agents/{qgsd-research-synthesizer.md → nf-research-synthesizer.md} +5 -5
  16. package/agents/{qgsd-roadmapper.md → nf-roadmapper.md} +3 -3
  17. package/agents/{qgsd-verifier.md → nf-verifier.md} +8 -8
  18. package/bin/accept-debug-invariant.cjs +2 -2
  19. package/bin/account-manager.cjs +10 -10
  20. package/bin/aggregate-requirements.cjs +1 -1
  21. package/bin/analyze-assumptions.cjs +3 -3
  22. package/bin/analyze-state-space.cjs +14 -14
  23. package/bin/assumption-register.cjs +146 -0
  24. package/bin/attribute-trace-divergence.cjs +1 -1
  25. package/bin/auth-drivers/gh-cli.cjs +1 -1
  26. package/bin/auth-drivers/pool.cjs +1 -1
  27. package/bin/autoClosePtoF.cjs +3 -3
  28. package/bin/budget-tracker.cjs +77 -0
  29. package/bin/build-layer-manifest.cjs +153 -0
  30. package/bin/call-quorum-slot.cjs +3 -3
  31. package/bin/ccr-secure-config.cjs +5 -5
  32. package/bin/check-bundled-sdks.cjs +1 -1
  33. package/bin/check-mcp-health.cjs +1 -1
  34. package/bin/check-provider-health.cjs +6 -6
  35. package/bin/check-spec-sync.cjs +26 -26
  36. package/bin/check-trace-schema-drift.cjs +5 -5
  37. package/bin/conformance-schema.cjs +2 -2
  38. package/bin/cross-layer-dashboard.cjs +297 -0
  39. package/bin/design-impact.cjs +377 -0
  40. package/bin/detect-coverage-gaps.cjs +7 -7
  41. package/bin/failure-mode-catalog.cjs +227 -0
  42. package/bin/failure-taxonomy.cjs +177 -0
  43. package/bin/formal-scope-scan.cjs +179 -0
  44. package/bin/gate-a-grounding.cjs +334 -0
  45. package/bin/gate-b-abstraction.cjs +243 -0
  46. package/bin/gate-c-validation.cjs +166 -0
  47. package/bin/generate-formal-specs.cjs +17 -17
  48. package/bin/generate-petri-net.cjs +3 -3
  49. package/bin/generate-tla-cfg.cjs +5 -5
  50. package/bin/git-heatmap.cjs +571 -0
  51. package/bin/harness-diagnostic.cjs +326 -0
  52. package/bin/hazard-model.cjs +261 -0
  53. package/bin/install-formal-tools.cjs +1 -1
  54. package/bin/install.js +184 -139
  55. package/bin/instrumentation-map.cjs +178 -0
  56. package/bin/invariant-catalog.cjs +437 -0
  57. package/bin/issue-classifier.cjs +2 -2
  58. package/bin/load-baseline-requirements.cjs +4 -4
  59. package/bin/manage-agents-core.cjs +32 -32
  60. package/bin/migrate-to-slots.cjs +39 -39
  61. package/bin/mismatch-register.cjs +217 -0
  62. package/bin/nForma.cjs +176 -81
  63. package/bin/{qgsd-solve.cjs → nf-solve.cjs} +327 -14
  64. package/bin/observe-config.cjs +8 -0
  65. package/bin/observe-debt-writer.cjs +1 -1
  66. package/bin/observe-handler-deps.cjs +356 -0
  67. package/bin/observe-handler-grafana.cjs +2 -17
  68. package/bin/observe-handler-internal.cjs +5 -5
  69. package/bin/observe-handler-logstash.cjs +2 -17
  70. package/bin/observe-handler-prometheus.cjs +2 -17
  71. package/bin/observe-handler-upstream.cjs +251 -0
  72. package/bin/observe-handlers.cjs +12 -33
  73. package/bin/observe-render.cjs +68 -22
  74. package/bin/observe-utils.cjs +37 -0
  75. package/bin/observed-fsm.cjs +324 -0
  76. package/bin/planning-paths.cjs +6 -0
  77. package/bin/polyrepo.cjs +1 -1
  78. package/bin/probe-quorum-slots.cjs +1 -1
  79. package/bin/promote-gate-maturity.cjs +274 -0
  80. package/bin/promote-model.cjs +1 -1
  81. package/bin/propose-debug-invariants.cjs +1 -1
  82. package/bin/quorum-cache.cjs +144 -0
  83. package/bin/quorum-consensus-gate.cjs +1 -1
  84. package/bin/quorum-preflight.cjs +89 -0
  85. package/bin/quorum-slot-dispatch.cjs +6 -6
  86. package/bin/requirements-core.cjs +1 -1
  87. package/bin/review-mcp-logs.cjs +1 -1
  88. package/bin/risk-heatmap.cjs +151 -0
  89. package/bin/run-account-manager-tlc.cjs +4 -4
  90. package/bin/run-account-pool-alloy.cjs +2 -2
  91. package/bin/run-alloy.cjs +2 -2
  92. package/bin/run-audit-alloy.cjs +2 -2
  93. package/bin/run-breaker-tlc.cjs +3 -3
  94. package/bin/run-formal-check.cjs +9 -9
  95. package/bin/run-formal-verify.cjs +30 -9
  96. package/bin/run-installer-alloy.cjs +2 -2
  97. package/bin/run-oscillation-tlc.cjs +4 -4
  98. package/bin/run-phase-tlc.cjs +1 -1
  99. package/bin/run-protocol-tlc.cjs +4 -4
  100. package/bin/run-quorum-composition-alloy.cjs +2 -2
  101. package/bin/run-sensitivity-sweep.cjs +2 -2
  102. package/bin/run-stop-hook-tlc.cjs +3 -3
  103. package/bin/run-tlc.cjs +21 -21
  104. package/bin/run-transcript-alloy.cjs +2 -2
  105. package/bin/secrets.cjs +5 -5
  106. package/bin/security-sweep.cjs +238 -0
  107. package/bin/sensitivity-report.cjs +3 -3
  108. package/bin/set-secret.cjs +5 -5
  109. package/bin/setup-telemetry-cron.sh +3 -3
  110. package/bin/stall-detector.cjs +126 -0
  111. package/bin/state-candidates.cjs +206 -0
  112. package/bin/sync-baseline-requirements.cjs +1 -1
  113. package/bin/telemetry-collector.cjs +1 -1
  114. package/bin/test-changed.cjs +111 -0
  115. package/bin/test-recipe-gen.cjs +250 -0
  116. package/bin/trace-corpus-stats.cjs +211 -0
  117. package/bin/unified-mcp-server.mjs +3 -3
  118. package/bin/update-scoreboard.cjs +1 -1
  119. package/bin/validate-memory.cjs +2 -2
  120. package/bin/validate-traces.cjs +10 -10
  121. package/bin/verify-quorum-health.cjs +66 -5
  122. package/bin/xstate-to-tla.cjs +4 -4
  123. package/bin/xstate-trace-walker.cjs +3 -3
  124. package/commands/{qgsd → nf}/add-phase.md +3 -3
  125. package/commands/{qgsd → nf}/add-requirement.md +3 -3
  126. package/commands/{qgsd → nf}/add-todo.md +3 -3
  127. package/commands/{qgsd → nf}/audit-milestone.md +4 -4
  128. package/commands/{qgsd → nf}/check-todos.md +3 -3
  129. package/commands/{qgsd → nf}/cleanup.md +3 -3
  130. package/commands/{qgsd → nf}/close-formal-gaps.md +2 -2
  131. package/commands/{qgsd → nf}/complete-milestone.md +9 -9
  132. package/commands/{qgsd → nf}/debug.md +9 -9
  133. package/commands/{qgsd → nf}/discuss-phase.md +3 -3
  134. package/commands/{qgsd → nf}/execute-phase.md +15 -15
  135. package/commands/{qgsd → nf}/fix-tests.md +3 -3
  136. package/commands/{qgsd → nf}/formal-test-sync.md +1 -1
  137. package/commands/{qgsd → nf}/health.md +3 -3
  138. package/commands/{qgsd → nf}/help.md +3 -3
  139. package/commands/{qgsd → nf}/insert-phase.md +3 -3
  140. package/commands/nf/join-discord.md +18 -0
  141. package/commands/{qgsd → nf}/list-phase-assumptions.md +2 -2
  142. package/commands/{qgsd → nf}/map-codebase.md +7 -7
  143. package/commands/{qgsd → nf}/map-requirements.md +3 -3
  144. package/commands/{qgsd → nf}/mcp-restart.md +3 -3
  145. package/commands/{qgsd → nf}/mcp-set-model.md +8 -8
  146. package/commands/{qgsd → nf}/mcp-setup.md +63 -63
  147. package/commands/{qgsd → nf}/mcp-status.md +3 -3
  148. package/commands/{qgsd → nf}/mcp-update.md +7 -7
  149. package/commands/{qgsd → nf}/new-milestone.md +8 -8
  150. package/commands/{qgsd → nf}/new-project.md +8 -8
  151. package/commands/{qgsd → nf}/observe.md +49 -16
  152. package/commands/{qgsd → nf}/pause-work.md +3 -3
  153. package/commands/{qgsd → nf}/plan-milestone-gaps.md +5 -5
  154. package/commands/{qgsd → nf}/plan-phase.md +6 -6
  155. package/commands/{qgsd → nf}/polyrepo.md +2 -2
  156. package/commands/{qgsd → nf}/progress.md +3 -3
  157. package/commands/{qgsd → nf}/queue.md +2 -2
  158. package/commands/{qgsd → nf}/quick.md +8 -8
  159. package/commands/{qgsd → nf}/quorum-test.md +10 -10
  160. package/commands/{qgsd → nf}/quorum.md +36 -86
  161. package/commands/{qgsd → nf}/reapply-patches.md +2 -2
  162. package/commands/{qgsd → nf}/remove-phase.md +3 -3
  163. package/commands/{qgsd → nf}/research-phase.md +12 -12
  164. package/commands/{qgsd → nf}/resume-work.md +3 -3
  165. package/commands/nf/review-requirements.md +31 -0
  166. package/commands/{qgsd → nf}/set-profile.md +3 -3
  167. package/commands/{qgsd → nf}/settings.md +6 -6
  168. package/commands/{qgsd → nf}/solve.md +35 -35
  169. package/commands/{qgsd → nf}/sync-baselines.md +4 -4
  170. package/commands/{qgsd → nf}/triage.md +10 -10
  171. package/commands/{qgsd → nf}/update.md +3 -3
  172. package/commands/{qgsd → nf}/verify-work.md +5 -5
  173. package/hooks/dist/config-loader.js +188 -32
  174. package/hooks/dist/conformance-schema.cjs +2 -2
  175. package/hooks/dist/gsd-context-monitor.js +118 -13
  176. package/hooks/dist/{qgsd-check-update.js → nf-check-update.js} +5 -5
  177. package/hooks/dist/{qgsd-circuit-breaker.js → nf-circuit-breaker.js} +35 -24
  178. package/hooks/dist/{qgsd-precompact.js → nf-precompact.js} +13 -13
  179. package/hooks/dist/{qgsd-prompt.js → nf-prompt.js} +110 -33
  180. package/hooks/dist/nf-session-start.js +185 -0
  181. package/hooks/dist/{qgsd-slot-correlator.js → nf-slot-correlator.js} +13 -5
  182. package/hooks/dist/{qgsd-spec-regen.js → nf-spec-regen.js} +17 -8
  183. package/hooks/dist/{qgsd-statusline.js → nf-statusline.js} +12 -3
  184. package/hooks/dist/{qgsd-stop.js → nf-stop.js} +152 -18
  185. package/hooks/dist/{qgsd-token-collector.js → nf-token-collector.js} +12 -4
  186. package/hooks/dist/unified-mcp-server.mjs +2 -2
  187. package/package.json +6 -4
  188. package/scripts/build-hooks.js +13 -6
  189. package/scripts/secret-audit.sh +1 -1
  190. package/scripts/verify-hooks-sync.cjs +90 -0
  191. package/templates/{qgsd.json → nf.json} +4 -4
  192. package/commands/qgsd/join-discord.md +0 -18
  193. package/hooks/dist/qgsd-session-start.js +0 -122
@@ -21,14 +21,15 @@
21
21
  // Traceability (3) — generate-traceability-matrix.cjs (requirements <-> properties matrix)
22
22
  // check-coverage-guard.cjs (coverage regression guard vs baseline)
23
23
  // analyze-state-space.cjs (state-space risk classification per TLA+ model)
24
+ // Gates (3) — gate-a-grounding.cjs, gate-b-abstraction.cjs, gate-c-validation.cjs
24
25
  // Registry (N) — custom check commands from model-registry.json
25
26
  // ─────────────────────────────────────────────────────────────
26
- // Total: 34+ steps (dynamic — registry can add more)
27
+ // Total: 37+ steps (dynamic — registry can add more)
27
28
  //
28
29
  // Usage:
29
30
  // node bin/run-formal-verify.cjs # all 28 steps
30
31
  // node bin/run-formal-verify.cjs --concurrent # run tool groups in parallel (old behavior)
31
- // QGSD_FORMAL_CONCURRENT=1 node bin/run-formal-verify.cjs # same via env var
32
+ // NF_FORMAL_CONCURRENT=1 node bin/run-formal-verify.cjs # same via env var
32
33
  // node bin/run-formal-verify.cjs --only=generate # source extraction only (2 steps)
33
34
  // node bin/run-formal-verify.cjs --only=tla # TLA+ only (10 steps)
34
35
  // node bin/run-formal-verify.cjs --only=alloy # Alloy only (8 steps)
@@ -63,7 +64,7 @@ for (const arg of process.argv.slice(2)) {
63
64
  }
64
65
 
65
66
  // ── Runner picker maps ─────────────────────────────────────────────────────────
66
- // Maps known QGSD model names to their specialized runners. Unknown models
67
+ // Maps known nForma model names to their specialized runners. Unknown models
67
68
  // fall back to generic runners (run-tlc.cjs, run-alloy.cjs, run-prism.cjs).
68
69
 
69
70
  const TLA_RUNNER_MAP = {
@@ -328,9 +329,9 @@ const STATIC_STEPS = [
328
329
  // ─ Source extraction — must run first so generated specs are fresh ──────────
329
330
  {
330
331
  tool: 'generate', id: 'generate:tla-from-xstate',
331
- label: 'Generate TLA+ spec (QGSDQuorum_xstate.tla) + TLC model config from XState machine (xstate-to-tla)',
332
+ label: 'Generate TLA+ spec (NFQuorum_xstate.tla) + TLC model config from XState machine (xstate-to-tla)',
332
333
  type: 'node', script: 'xstate-to-tla.cjs',
333
- args: ['src/machines/qgsd-workflow.machine.ts', '--module=QGSDQuorum', '--config=.planning/formal/tla/guards/qgsd-workflow.json'],
334
+ args: ['src/machines/nf-workflow.machine.ts', '--module=NFQuorum', '--config=.planning/formal/tla/guards/nf-workflow.json'],
334
335
  },
335
336
  {
336
337
  tool: 'generate', id: 'generate:alloy-prism-specs',
@@ -394,6 +395,26 @@ const STATIC_STEPS = [
394
395
  type: 'node', script: 'analyze-state-space.cjs', args: [],
395
396
  nonCritical: true,
396
397
  },
398
+
399
+ // ─ Gates — cross-layer alignment checks ───────────────────────────────────
400
+ {
401
+ tool: 'gates', id: 'gates:gate-a',
402
+ label: 'Gate A -- L1-L2 grounding alignment score',
403
+ type: 'node', script: 'gate-a-grounding.cjs', args: [],
404
+ nonCritical: true,
405
+ },
406
+ {
407
+ tool: 'gates', id: 'gates:gate-b',
408
+ label: 'Gate B -- L2-L3 traceability alignment score',
409
+ type: 'node', script: 'gate-b-abstraction.cjs', args: [],
410
+ nonCritical: true,
411
+ },
412
+ {
413
+ tool: 'gates', id: 'gates:gate-c',
414
+ label: 'Gate C -- L3-TC validation alignment score',
415
+ type: 'node', script: 'gate-c-validation.cjs', args: [],
416
+ nonCritical: true,
417
+ },
397
418
  ];
398
419
 
399
420
  // Discover dynamic model steps from ROOT/.planning/formal/
@@ -412,7 +433,7 @@ process.stdout.write(TAG + ' Discovered models: ' + uniqueDynamicSteps.length +
412
433
  const argv = process.argv.slice(2);
413
434
  const onlyArg = argv.find(a => a.startsWith('--only='));
414
435
  const only = onlyArg ? onlyArg.split('=')[1] : null;
415
- const concurrent = argv.includes('--concurrent') || process.env.QGSD_FORMAL_CONCURRENT === '1';
436
+ const concurrent = argv.includes('--concurrent') || process.env.NF_FORMAL_CONCURRENT === '1';
416
437
 
417
438
  const steps = only
418
439
  ? STEPS.filter(s => s.tool === only || s.id === only)
@@ -421,7 +442,7 @@ const steps = only
421
442
  if (only && steps.length === 0) {
422
443
  process.stderr.write(
423
444
  TAG + ' Unknown --only value: ' + only + '\n' +
424
- TAG + ' Valid values: tla, alloy, prism, petri, generate, ci, uppaal, registry, or a step id\n'
445
+ TAG + ' Valid values: tla, alloy, prism, petri, generate, ci, uppaal, gates, registry, or a step id\n'
425
446
  );
426
447
  process.exit(1);
427
448
  }
@@ -550,7 +571,7 @@ async function runOnce() {
550
571
  fs.writeFileSync(ndjsonPath, '', 'utf8');
551
572
 
552
573
  process.stdout.write(TAG + ' ' + HR + '\n');
553
- process.stdout.write(TAG + ' QGSD Formal Verification Suite\n');
574
+ process.stdout.write(TAG + ' nForma Formal Verification Suite\n');
554
575
  if (only) {
555
576
  process.stdout.write(TAG + ' Filter: --only=' + only + '\n');
556
577
  }
@@ -641,7 +662,7 @@ if (watchArg) {
641
662
  // by spawning with a custom cwd. __dirname-relative paths would always point
642
663
  // to the real repo's src/machines/ regardless of spawn cwd, breaking isolation.
643
664
  const machineDir = path.join(process.cwd(), 'src', 'machines');
644
- const machineName = 'qgsd-workflow.machine.ts';
665
+ const machineName = 'nf-workflow.machine.ts';
645
666
  let debounceTimer = null;
646
667
  let running = false; // concurrent-run guard
647
668
  let watcher = null;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  // bin/run-installer-alloy.cjs
4
- // Invokes Alloy 6 JAR headless for QGSD installer and taxonomy specs (GAP-7, GAP-8).
4
+ // Invokes Alloy 6 JAR headless for nForma installer and taxonomy specs (GAP-7, GAP-8).
5
5
  // Requirements: GAP-7, GAP-8
6
6
  //
7
7
  // Usage:
@@ -16,7 +16,7 @@
16
16
  // - .planning/formal/alloy/org.alloytools.alloy.dist.jar (see VERIFICATION_TOOLS.md for download)
17
17
 
18
18
  const { spawnSync } = require('child_process');
19
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
19
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
20
20
  const fs = require('fs');
21
21
  const path = require('path');
22
22
  const { writeCheckResult } = require('./write-check-result.cjs');
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  // bin/run-oscillation-tlc.cjs
4
- // Invokes TLC model checker for QGSD oscillation and convergence TLA+ specifications.
4
+ // Invokes TLC model checker for nForma oscillation and convergence TLA+ specifications.
5
5
  // Requirements: GAP-1, GAP-5
6
6
  //
7
7
  // Usage:
@@ -14,7 +14,7 @@
14
14
  // - .planning/formal/tla/tla2tools.jar (see .planning/formal/tla/README.md for download command)
15
15
 
16
16
  const { spawnSync } = require('child_process');
17
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
17
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
20
  const { writeCheckResult } = require('./write-check-result.cjs');
@@ -129,8 +129,8 @@ if (!fs.existsSync(jarPath)) {
129
129
 
130
130
  // ── 4. Resolve spec and config paths ─────────────────────────────────────────
131
131
  const specFileName = configName === 'MCoscillation'
132
- ? 'QGSDOscillation.tla'
133
- : 'QGSDConvergence.tla';
132
+ ? 'NFOscillation.tla'
133
+ : 'NFConvergence.tla';
134
134
  const specPath = path.join(ROOT, '.planning', 'formal', 'tla', specFileName);
135
135
  const cfgPath = path.join(ROOT, '.planning', 'formal', 'tla', configName + '.cfg');
136
136
 
@@ -15,7 +15,7 @@
15
15
  // NOTE: Uses spawnSync (no shell) for safe subprocess invocation -- no exec().
16
16
 
17
17
  const { spawnSync } = require('child_process');
18
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
18
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
19
19
  const fs = require('fs');
20
20
  const path = require('path');
21
21
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  // bin/run-protocol-tlc.cjs
4
- // Invokes TLC model checker for QGSD protocol termination TLA+ specifications.
4
+ // Invokes TLC model checker for nForma protocol termination TLA+ specifications.
5
5
  // Requirements: GAP-2, GAP-6
6
6
  //
7
7
  // Usage:
@@ -14,7 +14,7 @@
14
14
  // - .planning/formal/tla/tla2tools.jar (see .planning/formal/tla/README.md for download command)
15
15
 
16
16
  const { spawnSync } = require('child_process');
17
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
17
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
20
  const { writeCheckResult } = require('./write-check-result.cjs');
@@ -129,8 +129,8 @@ if (!fs.existsSync(jarPath)) {
129
129
 
130
130
  // ── 4. Resolve spec and config paths ─────────────────────────────────────────
131
131
  const specFileName = configName === 'MCdeliberation'
132
- ? 'QGSDDeliberation.tla'
133
- : 'QGSDPreFilter.tla';
132
+ ? 'NFDeliberation.tla'
133
+ : 'NFPreFilter.tla';
134
134
  const specPath = path.join(ROOT, '.planning', 'formal', 'tla', specFileName);
135
135
  const cfgPath = path.join(ROOT, '.planning', 'formal', 'tla', configName + '.cfg');
136
136
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  // bin/run-quorum-composition-alloy.cjs
4
- // Invokes Alloy 6 JAR headless for the QGSD quorum composition model.
4
+ // Invokes Alloy 6 JAR headless for the nForma quorum composition model.
5
5
  // Requirements: SPEC-03
6
6
  //
7
7
  // Usage:
@@ -12,7 +12,7 @@
12
12
  // - .planning/formal/alloy/org.alloytools.alloy.dist.jar (see VERIFICATION_TOOLS.md for download)
13
13
 
14
14
  const { spawnSync } = require('child_process');
15
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
15
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
16
16
  const fs = require('fs');
17
17
  const path = require('path');
18
18
  const { writeCheckResult } = require('./write-check-result.cjs');
@@ -13,7 +13,7 @@
13
13
  // Always exits 0.
14
14
 
15
15
  const { spawnSync } = require('child_process');
16
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
16
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
17
17
  const fs = require('fs');
18
18
  const path = require('path');
19
19
  const os = require('os');
@@ -67,7 +67,7 @@ function runTLCSweep(maxSize) {
67
67
  const overrideCfg = baseCfg.replace(/MaxSize\s*=\s*\d+/, 'MaxSize = ' + maxSize);
68
68
  fs.writeFileSync(tmpCfg, overrideCfg, 'utf8');
69
69
 
70
- const tlaFile = path.join(__dirname, '..', '.planning', 'formal', 'tla', 'QGSDQuorum.tla');
70
+ const tlaFile = path.join(__dirname, '..', '.planning', 'formal', 'tla', 'NFQuorum.tla');
71
71
  process.stderr.write('[heap] Xms=64m Xmx=' + JAVA_HEAP_MAX + '\n');
72
72
  const javaResult = spawnSync('java', [
73
73
  '-Xms64m', '-Xmx' + JAVA_HEAP_MAX,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  // bin/run-stop-hook-tlc.cjs
4
- // Invokes TLC model checker for the QGSD Stop hook TLA+ specification.
4
+ // Invokes TLC model checker for the nForma Stop hook TLA+ specification.
5
5
  // Requirements: SPEC-01
6
6
  //
7
7
  // Usage:
@@ -13,7 +13,7 @@
13
13
  // - .planning/formal/tla/tla2tools.jar (see .planning/formal/tla/README.md for download command)
14
14
 
15
15
  const { spawnSync } = require('child_process');
16
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
16
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
17
17
  const fs = require('fs');
18
18
  const path = require('path');
19
19
  const { writeCheckResult } = require('./write-check-result.cjs');
@@ -118,7 +118,7 @@ if (!fs.existsSync(jarPath)) {
118
118
  }
119
119
 
120
120
  // ── 4. Resolve spec and config paths ─────────────────────────────────────────
121
- const specPath = path.join(ROOT, '.planning', 'formal', 'tla', 'QGSDStopHook.tla');
121
+ const specPath = path.join(ROOT, '.planning', 'formal', 'tla', 'NFStopHook.tla');
122
122
  const cfgPath = path.join(ROOT, '.planning', 'formal', 'tla', configName + '.cfg');
123
123
 
124
124
  // LivenessProperty1/2/3 requires -workers 1 (TLC multi-worker liveness bug in v1.8.0)
package/bin/run-tlc.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  // bin/run-tlc.cjs
4
- // Invokes TLC model checker for the QGSD formal TLA+ specification.
4
+ // Invokes TLC model checker for the nForma formal TLA+ specification.
5
5
  // Requirements: TLA-04
6
6
  //
7
7
  // Usage:
@@ -14,7 +14,7 @@
14
14
  // - .planning/formal/tla/tla2tools.jar (see .planning/formal/tla/README.md for download command)
15
15
 
16
16
  const { spawnSync } = require('child_process');
17
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
17
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
20
  const { writeCheckResult } = require('./write-check-result.cjs');
@@ -298,52 +298,52 @@ if (require.main === module) {
298
298
  // Map config names to their corresponding spec files.
299
299
  // Static map for known exceptions; auto-discovery handles the rest.
300
300
  const SPEC_MAP = {
301
- 'MCMCPEnv': 'QGSDMCPEnv.tla',
302
- 'MCsafety': 'QGSDQuorum.tla',
303
- 'MCliveness': 'QGSDQuorum.tla',
304
- 'MCQGSDQuorum': 'QGSDQuorum_xstate.tla',
305
- 'MCrecruiting-liveness': 'QGSDRecruiting.tla',
306
- 'MCrecruiting-safety': 'QGSDRecruiting.tla',
301
+ 'MCMCPEnv': 'NFMCPEnv.tla',
302
+ 'MCsafety': 'NFQuorum.tla',
303
+ 'MCliveness': 'NFQuorum.tla',
304
+ 'MCNFQuorum': 'NFQuorum_xstate.tla',
305
+ 'MCrecruiting-liveness': 'NFRecruiting.tla',
306
+ 'MCrecruiting-safety': 'NFRecruiting.tla',
307
307
  'MCTUINavigation': 'TUINavigation.tla',
308
308
  };
309
309
 
310
- // Auto-discover spec file: (1) check SPEC_MAP, (2) scan cfg header for QGSD*.tla ref,
311
- // (3) try naming conventions, (4) fall back to QGSDQuorum.tla
310
+ // Auto-discover spec file: (1) check SPEC_MAP, (2) scan cfg header for nForma*.tla ref,
311
+ // (3) try naming conventions, (4) fall back to NFQuorum.tla
312
312
  function resolveSpecFile(cfgName) {
313
313
  if (SPEC_MAP[cfgName]) return SPEC_MAP[cfgName];
314
314
 
315
315
  const tlaDir = path.join(ROOT, '.planning', 'formal', 'tla');
316
316
 
317
- // Strategy 1: read cfg header for QGSD*.tla reference (with or without .tla suffix)
317
+ // Strategy 1: read cfg header for nForma*.tla reference (with or without .tla suffix)
318
318
  try {
319
319
  const cfgContent = fs.readFileSync(path.join(tlaDir, cfgName + '.cfg'), 'utf8');
320
320
  const headerLines = cfgContent.split('\n').slice(0, 5).join('\n');
321
- // Match QGSD*.tla or "for QGSD*." (without .tla extension)
322
- const refMatch = headerLines.match(/QGSD\w+\.tla/) || headerLines.match(/QGSD\w+/);
321
+ // Match NF*.tla or "for nForma*." (without .tla extension)
322
+ const refMatch = headerLines.match(/NF\w+\.tla/) || headerLines.match(/NF\w+/);
323
323
  if (refMatch) {
324
324
  const candidate = refMatch[0].endsWith('.tla') ? refMatch[0] : refMatch[0] + '.tla';
325
325
  if (fs.existsSync(path.join(tlaDir, candidate))) return candidate;
326
326
  }
327
327
  } catch (_) { /* fall through */ }
328
328
 
329
- // Strategy 2: naming convention — strip MC prefix, normalize hyphens, find matching QGSD*.tla
329
+ // Strategy 2: naming convention — strip MC prefix, normalize hyphens, find matching NF*.tla
330
330
  const stripped = cfgName.replace(/^MC/, '').toLowerCase().replace(/-/g, '');
331
331
  try {
332
332
  const allTla = fs.readdirSync(tlaDir).filter(f => f.endsWith('.tla') && !f.includes('TTrace'));
333
- const qgsdFiles = allTla.filter(f => f.startsWith('QGSD'));
333
+ const nfFiles = allTla.filter(f => f.startsWith('NF'));
334
334
  const normalize = (s) => s.toLowerCase().replace(/-/g, '');
335
- // Exact match against QGSD-prefixed files
336
- const match = qgsdFiles.find(f => normalize(f.replace('QGSD', '').replace('.tla', '')) === stripped);
335
+ // Exact match against nForma-prefixed files
336
+ const match = nfFiles.find(f => normalize(f.replace('NF', '').replace('.tla', '')) === stripped);
337
337
  if (match) return match;
338
- // Fuzzy substring match against QGSD-prefixed files
339
- const fuzzy = qgsdFiles.find(f => normalize(f).includes(stripped));
338
+ // Fuzzy substring match against nForma-prefixed files
339
+ const fuzzy = nfFiles.find(f => normalize(f).includes(stripped));
340
340
  if (fuzzy) return fuzzy;
341
- // Fallback: check non-QGSD-prefixed files (exact match on stripped name)
341
+ // Fallback: check non-nForma-prefixed files (exact match on stripped name)
342
342
  const nonPrefixed = allTla.find(f => normalize(f.replace('.tla', '')) === stripped);
343
343
  if (nonPrefixed) return nonPrefixed;
344
344
  } catch (_) { /* fall through */ }
345
345
 
346
- return 'QGSDQuorum.tla';
346
+ return 'NFQuorum.tla';
347
347
  }
348
348
 
349
349
  const specFile = resolveSpecFile(configName);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  // bin/run-transcript-alloy.cjs
4
- // Invokes Alloy 6 JAR headless for QGSD transcript scanning spec (GAP-4).
4
+ // Invokes Alloy 6 JAR headless for nForma transcript scanning spec (GAP-4).
5
5
  // Requirements: GAP-4
6
6
  //
7
7
  // Usage:
@@ -15,7 +15,7 @@
15
15
  // - .planning/formal/alloy/org.alloytools.alloy.dist.jar (see VERIFICATION_TOOLS.md for download)
16
16
 
17
17
  const { spawnSync } = require('child_process');
18
- const JAVA_HEAP_MAX = process.env.QGSD_JAVA_HEAP_MAX || '512m';
18
+ const JAVA_HEAP_MAX = process.env.NF_JAVA_HEAP_MAX || '512m';
19
19
  const fs = require('fs');
20
20
  const path = require('path');
21
21
  const { writeCheckResult } = require('./write-check-result.cjs');
package/bin/secrets.cjs CHANGED
@@ -2,9 +2,9 @@
2
2
  const os = require('os');
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const SERVICE = 'qgsd';
5
+ const SERVICE = 'nforma';
6
6
 
7
- const INDEX_PATH = path.join(os.homedir(), '.claude', 'qgsd-key-index.json');
7
+ const INDEX_PATH = path.join(os.homedir(), '.claude', 'nf-key-index.json');
8
8
 
9
9
  // Read the key index (no keychain access needed — just a JSON file)
10
10
  function readIndex() {
@@ -84,7 +84,7 @@ async function syncToClaudeJson(service) {
84
84
  try {
85
85
  credentials = await list(service);
86
86
  } catch (e) {
87
- process.stderr.write('[qgsd-secrets] keytar unavailable: ' + e.message + '\n');
87
+ process.stderr.write('[nf-secrets] keytar unavailable: ' + e.message + '\n');
88
88
  return;
89
89
  }
90
90
 
@@ -100,7 +100,7 @@ async function syncToClaudeJson(service) {
100
100
  try {
101
101
  raw = fs.readFileSync(claudeJsonPath, 'utf8');
102
102
  } catch (e) {
103
- process.stderr.write('[qgsd-secrets] ~/.claude.json not found, skipping sync\n');
103
+ process.stderr.write('[nf-secrets] ~/.claude.json not found, skipping sync\n');
104
104
  return;
105
105
  }
106
106
 
@@ -108,7 +108,7 @@ async function syncToClaudeJson(service) {
108
108
  try {
109
109
  claudeJson = JSON.parse(raw);
110
110
  } catch (e) {
111
- process.stderr.write('[qgsd-secrets] ~/.claude.json is invalid JSON, skipping sync\n');
111
+ process.stderr.write('[nf-secrets] ~/.claude.json is invalid JSON, skipping sync\n');
112
112
  return;
113
113
  }
114
114
 
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * bin/security-sweep.cjs — Standalone security scanner for nForma.
6
+ *
7
+ * Scans git-tracked files for hardcoded secrets, debug artifacts, and API keys.
8
+ * Produces structured findings with file:line references for VERIFICATION.md.
9
+ *
10
+ * Advisory only — exit code 0 always. Never blocks.
11
+ *
12
+ * Exports: SECRET_PATTERNS, scanFile, scanDirectory, formatReport
13
+ * CLI: node bin/security-sweep.cjs [--json] [--cwd /path]
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ // spawnSync is used (not exec) to avoid shell injection — arguments are passed as an array
19
+ const { spawnSync } = require('child_process');
20
+
21
+ // ─── Secret Patterns ────────────────────────────────────────────────────────
22
+
23
+ const SECRET_PATTERNS = [
24
+ { name: 'AWS Access Key', pattern: /AKIA[A-Z0-9]{16}/, severity: 'high' },
25
+ { name: 'GitHub Token', pattern: /gh[ps]_[A-Za-z0-9_]{36,}/, severity: 'high' },
26
+ { name: 'GitHub PAT', pattern: /github_pat_[A-Za-z0-9_]{22,}/, severity: 'high' },
27
+ { name: 'Stripe Live Key', pattern: /[sr]k_live_[A-Za-z0-9]{20,}/, severity: 'high' },
28
+ { name: 'OpenAI Key', pattern: /sk-[A-Za-z0-9]{32,}/, severity: 'high' },
29
+ { name: 'Generic API Key Assignment', pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"][^'"]{10,}['"]/i, severity: 'medium' },
30
+ { name: 'Generic Secret Assignment', pattern: /(?:secret|password)\s*[:=]\s*['"][^'"]{8,}['"]/i, severity: 'medium' },
31
+ { name: 'Debugger Statement', pattern: /^\s*debugger\s*;?\s*$/, severity: 'low' },
32
+ { name: 'Sensitive Console.log', pattern: /console\.log\(.*(?:password|secret|token|key|api_key)/i, severity: 'low' },
33
+ ];
34
+
35
+ // Words that indicate a line is a test fixture / not a real secret
36
+ const TEST_INDICATOR_WORDS = ['test', 'mock', 'fake', 'example', 'dummy', 'fixture', 'placeholder', 'todo'];
37
+
38
+ // File patterns to exclude from scanning
39
+ const EXCLUDE_PATTERNS = [
40
+ /\.test\./,
41
+ /\.spec\./,
42
+ /security-sweep\.cjs$/,
43
+ /\.md$/,
44
+ /\.json$/,
45
+ /\.jsonl$/,
46
+ /node_modules\//,
47
+ /\.planning\/\.quorum-cache\//,
48
+ /package-lock\.json$/,
49
+ ];
50
+
51
+ // ─── scanFile ────────────────────────────────────────────────────────────────
52
+
53
+ /**
54
+ * Scan file content line-by-line against SECRET_PATTERNS.
55
+ * @param {string} filePath - Relative or absolute file path (for reporting).
56
+ * @param {string} content - File content to scan.
57
+ * @returns {Array<{file:string, line:number, column:number, pattern_name:string, severity:string, match:string}>}
58
+ */
59
+ function scanFile(filePath, content) {
60
+ if (!content || typeof content !== 'string') return [];
61
+
62
+ // Skip binary content (null bytes in first 512 chars)
63
+ if (content.slice(0, 512).includes('\0')) return [];
64
+
65
+ const findings = [];
66
+ const lines = content.split('\n');
67
+
68
+ for (let i = 0; i < lines.length; i++) {
69
+ const line = lines[i];
70
+ const lineLower = line.toLowerCase();
71
+
72
+ // Skip lines containing test indicator words
73
+ if (TEST_INDICATOR_WORDS.some(w => lineLower.includes(w))) continue;
74
+
75
+ for (const pat of SECRET_PATTERNS) {
76
+ const m = pat.pattern.exec(line);
77
+ if (m) {
78
+ findings.push({
79
+ file: filePath,
80
+ line: i + 1,
81
+ column: m.index + 1,
82
+ pattern_name: pat.name,
83
+ severity: pat.severity,
84
+ match: m[0].length > 40 ? m[0].slice(0, 37) + '...' : m[0],
85
+ });
86
+ }
87
+ }
88
+ }
89
+
90
+ return findings;
91
+ }
92
+
93
+ // ─── scanDirectory ───────────────────────────────────────────────────────────
94
+
95
+ /**
96
+ * Scan git-tracked files in a directory.
97
+ * @param {string} cwd - Working directory to scan.
98
+ * @param {Object} [options]
99
+ * @param {string[]} [options.excludePatterns] - Additional glob patterns to exclude.
100
+ * @param {number} [options.maxFiles=500] - Max files to scan.
101
+ * @returns {{findings: Array, files_scanned: number, duration_ms: number}}
102
+ */
103
+ function scanDirectory(cwd, options = {}) {
104
+ const maxFiles = options.maxFiles || 500;
105
+ const startTime = Date.now();
106
+
107
+ // Get tracked files via git ls-files (spawnSync, not exec — no shell injection)
108
+ let files = [];
109
+ try {
110
+ const result = spawnSync('git', ['ls-files'], {
111
+ cwd,
112
+ encoding: 'utf8',
113
+ timeout: 5000,
114
+ });
115
+ if (result.status === 0 && result.stdout) {
116
+ files = result.stdout.split('\n').filter(f => f.trim());
117
+ }
118
+ } catch (_) {
119
+ return { findings: [], files_scanned: 0, duration_ms: Date.now() - startTime };
120
+ }
121
+
122
+ // Filter out excluded patterns
123
+ files = files.filter(f => !EXCLUDE_PATTERNS.some(rx => rx.test(f)));
124
+
125
+ // Apply additional exclude patterns from options
126
+ if (options.excludePatterns && Array.isArray(options.excludePatterns)) {
127
+ for (const pat of options.excludePatterns) {
128
+ try {
129
+ const rx = new RegExp(pat);
130
+ files = files.filter(f => !rx.test(f));
131
+ } catch (_) {}
132
+ }
133
+ }
134
+
135
+ // Cap file count
136
+ if (files.length > maxFiles) {
137
+ files = files.slice(0, maxFiles);
138
+ }
139
+
140
+ const allFindings = [];
141
+ let scanned = 0;
142
+
143
+ for (const relPath of files) {
144
+ const absPath = path.join(cwd, relPath);
145
+ try {
146
+ const content = fs.readFileSync(absPath, 'utf8');
147
+ // Skip binary files (null bytes in first 512 bytes)
148
+ if (content.slice(0, 512).includes('\0')) continue;
149
+ scanned++;
150
+ const findings = scanFile(relPath, content);
151
+ allFindings.push(...findings);
152
+ } catch (_) {
153
+ // Skip unreadable files silently
154
+ }
155
+ }
156
+
157
+ return {
158
+ findings: allFindings,
159
+ files_scanned: scanned,
160
+ duration_ms: Date.now() - startTime,
161
+ };
162
+ }
163
+
164
+ // ─── formatReport ────────────────────────────────────────────────────────────
165
+
166
+ /**
167
+ * Format scan results as a markdown section for VERIFICATION.md.
168
+ * @param {{findings: Array, files_scanned: number, duration_ms: number}} scanResult
169
+ * @returns {string}
170
+ */
171
+ function formatReport(scanResult) {
172
+ const { findings, files_scanned, duration_ms } = scanResult;
173
+ const lines = [];
174
+
175
+ lines.push('## Security Sweep');
176
+ lines.push('');
177
+ lines.push(`**Scanned:** ${files_scanned} files in ${duration_ms}ms`);
178
+
179
+ if (!findings || findings.length === 0) {
180
+ lines.push('**Findings:** 0');
181
+ lines.push('');
182
+ lines.push('No hardcoded secrets, debug artifacts, or API keys detected.');
183
+ return lines.join('\n');
184
+ }
185
+
186
+ const high = findings.filter(f => f.severity === 'high').length;
187
+ const medium = findings.filter(f => f.severity === 'medium').length;
188
+ const low = findings.filter(f => f.severity === 'low').length;
189
+
190
+ lines.push(`**Findings:** ${findings.length} (${high} high, ${medium} medium, ${low} low)`);
191
+ lines.push('');
192
+ lines.push('| Severity | File | Line | Pattern | Match |');
193
+ lines.push('|----------|------|------|---------|-------|');
194
+
195
+ for (const f of findings) {
196
+ lines.push(`| ${f.severity} | ${f.file} | ${f.line} | ${f.pattern_name} | ${f.match} |`);
197
+ }
198
+
199
+ lines.push('');
200
+ lines.push('_Advisory: Review findings and confirm whether they are genuine secrets or false positives._');
201
+
202
+ return lines.join('\n');
203
+ }
204
+
205
+ // ─── CLI ─────────────────────────────────────────────────────────────────────
206
+
207
+ if (require.main === module) {
208
+ const args = process.argv.slice(2);
209
+ const jsonFlag = args.includes('--json');
210
+ const cwdIdx = args.indexOf('--cwd');
211
+ const cwd = cwdIdx >= 0 && args[cwdIdx + 1] ? args[cwdIdx + 1] : process.cwd();
212
+
213
+ const result = scanDirectory(cwd);
214
+
215
+ if (jsonFlag) {
216
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
217
+ } else {
218
+ process.stdout.write(formatReport(result) + '\n');
219
+ }
220
+
221
+ // Log conformance event (best-effort)
222
+ try {
223
+ const planningPaths = require('./planning-paths.cjs');
224
+ const conformancePath = planningPaths.resolveWithFallback(cwd, 'conformance-events');
225
+ const event = {
226
+ ts: new Date().toISOString(),
227
+ action: 'security_sweep',
228
+ files_scanned: result.files_scanned,
229
+ findings_count: result.findings.length,
230
+ duration_ms: result.duration_ms,
231
+ };
232
+ fs.appendFileSync(conformancePath, JSON.stringify(event) + '\n');
233
+ } catch (_) {}
234
+
235
+ process.exit(0);
236
+ }
237
+
238
+ module.exports = { SECRET_PATTERNS, scanFile, scanDirectory, formatReport };