@nforma.ai/nforma 0.2.1

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 (215) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1024 -0
  3. package/agents/qgsd-codebase-mapper.md +764 -0
  4. package/agents/qgsd-debugger.md +1201 -0
  5. package/agents/qgsd-executor.md +472 -0
  6. package/agents/qgsd-integration-checker.md +443 -0
  7. package/agents/qgsd-phase-researcher.md +502 -0
  8. package/agents/qgsd-plan-checker.md +643 -0
  9. package/agents/qgsd-planner.md +1182 -0
  10. package/agents/qgsd-project-researcher.md +621 -0
  11. package/agents/qgsd-quorum-orchestrator.md +628 -0
  12. package/agents/qgsd-quorum-slot-worker.md +41 -0
  13. package/agents/qgsd-quorum-synthesizer.md +133 -0
  14. package/agents/qgsd-quorum-test-worker.md +37 -0
  15. package/agents/qgsd-quorum-worker.md +161 -0
  16. package/agents/qgsd-research-synthesizer.md +239 -0
  17. package/agents/qgsd-roadmapper.md +660 -0
  18. package/agents/qgsd-verifier.md +628 -0
  19. package/bin/accept-debug-invariant.cjs +165 -0
  20. package/bin/account-manager.cjs +719 -0
  21. package/bin/aggregate-requirements.cjs +466 -0
  22. package/bin/analyze-assumptions.cjs +757 -0
  23. package/bin/analyze-state-space.cjs +921 -0
  24. package/bin/attribute-trace-divergence.cjs +150 -0
  25. package/bin/auth-drivers/gh-cli.cjs +93 -0
  26. package/bin/auth-drivers/index.cjs +46 -0
  27. package/bin/auth-drivers/pool.cjs +67 -0
  28. package/bin/auth-drivers/simple.cjs +95 -0
  29. package/bin/autoClosePtoF.cjs +110 -0
  30. package/bin/blessed-terminal.cjs +350 -0
  31. package/bin/build-phase-index.cjs +472 -0
  32. package/bin/call-quorum-slot.cjs +541 -0
  33. package/bin/ccr-secure-config.cjs +99 -0
  34. package/bin/ccr-secure-start.cjs +83 -0
  35. package/bin/check-bundled-sdks.cjs +177 -0
  36. package/bin/check-coverage-guard.cjs +112 -0
  37. package/bin/check-liveness-fairness.cjs +95 -0
  38. package/bin/check-mcp-health.cjs +123 -0
  39. package/bin/check-provider-health.cjs +395 -0
  40. package/bin/check-results-exit.cjs +24 -0
  41. package/bin/check-spec-sync.cjs +360 -0
  42. package/bin/check-trace-redaction.cjs +271 -0
  43. package/bin/check-trace-schema-drift.cjs +99 -0
  44. package/bin/compareDrift.cjs +21 -0
  45. package/bin/conformance-schema.cjs +12 -0
  46. package/bin/count-scenarios.cjs +420 -0
  47. package/bin/debt-dedup.cjs +144 -0
  48. package/bin/debt-ledger.cjs +61 -0
  49. package/bin/debt-retention.cjs +76 -0
  50. package/bin/debt-state-machine.cjs +80 -0
  51. package/bin/detect-coverage-gaps.cjs +204 -0
  52. package/bin/detect-project-intent.cjs +362 -0
  53. package/bin/export-prism-constants.cjs +164 -0
  54. package/bin/extract-annotations.cjs +633 -0
  55. package/bin/extractFormalExpected.cjs +104 -0
  56. package/bin/fingerprint-drift.cjs +24 -0
  57. package/bin/fingerprint-issue.cjs +46 -0
  58. package/bin/formal-core.cjs +519 -0
  59. package/bin/formal-ref-linker.cjs +141 -0
  60. package/bin/formal-test-sync.cjs +788 -0
  61. package/bin/generate-formal-specs.cjs +588 -0
  62. package/bin/generate-petri-net.cjs +397 -0
  63. package/bin/generate-phase-spec.cjs +249 -0
  64. package/bin/generate-proposed-changes.cjs +194 -0
  65. package/bin/generate-tla-cfg.cjs +122 -0
  66. package/bin/generate-traceability-matrix.cjs +701 -0
  67. package/bin/generate-triage-bundle.cjs +300 -0
  68. package/bin/gh-account-rotate.cjs +34 -0
  69. package/bin/initialize-model-registry.cjs +105 -0
  70. package/bin/install-formal-tools.cjs +382 -0
  71. package/bin/install.js +2424 -0
  72. package/bin/isNumericThreshold.cjs +34 -0
  73. package/bin/issue-classifier.cjs +151 -0
  74. package/bin/levenshtein.cjs +74 -0
  75. package/bin/lint-formal-models.cjs +580 -0
  76. package/bin/load-baseline-requirements.cjs +275 -0
  77. package/bin/manage-agents-core.cjs +815 -0
  78. package/bin/migrate-formal-dir.cjs +172 -0
  79. package/bin/migrate-planning.cjs +206 -0
  80. package/bin/migrate-to-slots.cjs +255 -0
  81. package/bin/nForma.cjs +2726 -0
  82. package/bin/observe-config.cjs +353 -0
  83. package/bin/observe-debt-writer.cjs +140 -0
  84. package/bin/observe-handler-grafana.cjs +128 -0
  85. package/bin/observe-handler-internal.cjs +301 -0
  86. package/bin/observe-handler-logstash.cjs +153 -0
  87. package/bin/observe-handler-prometheus.cjs +185 -0
  88. package/bin/observe-handlers.cjs +436 -0
  89. package/bin/observe-registry.cjs +131 -0
  90. package/bin/observe-render.cjs +168 -0
  91. package/bin/planning-paths.cjs +167 -0
  92. package/bin/polyrepo.cjs +560 -0
  93. package/bin/prism-priority.cjs +153 -0
  94. package/bin/probe-quorum-slots.cjs +167 -0
  95. package/bin/promote-model.cjs +225 -0
  96. package/bin/propose-debug-invariants.cjs +165 -0
  97. package/bin/providers.json +392 -0
  98. package/bin/pty-proxy.py +129 -0
  99. package/bin/qgsd-solve.cjs +2477 -0
  100. package/bin/quorum-consensus-gate.cjs +238 -0
  101. package/bin/quorum-formal-context.cjs +183 -0
  102. package/bin/quorum-slot-dispatch.cjs +934 -0
  103. package/bin/read-policy.cjs +60 -0
  104. package/bin/requirement-map.cjs +63 -0
  105. package/bin/requirements-core.cjs +247 -0
  106. package/bin/resolve-cli.cjs +101 -0
  107. package/bin/review-mcp-logs.cjs +294 -0
  108. package/bin/run-account-manager-tlc.cjs +188 -0
  109. package/bin/run-account-pool-alloy.cjs +158 -0
  110. package/bin/run-alloy.cjs +153 -0
  111. package/bin/run-audit-alloy.cjs +187 -0
  112. package/bin/run-breaker-tlc.cjs +181 -0
  113. package/bin/run-formal-check.cjs +395 -0
  114. package/bin/run-formal-verify.cjs +701 -0
  115. package/bin/run-installer-alloy.cjs +188 -0
  116. package/bin/run-oauth-rotation-prism.cjs +132 -0
  117. package/bin/run-oscillation-tlc.cjs +202 -0
  118. package/bin/run-phase-tlc.cjs +228 -0
  119. package/bin/run-prism.cjs +446 -0
  120. package/bin/run-protocol-tlc.cjs +201 -0
  121. package/bin/run-quorum-composition-alloy.cjs +155 -0
  122. package/bin/run-sensitivity-sweep.cjs +231 -0
  123. package/bin/run-stop-hook-tlc.cjs +188 -0
  124. package/bin/run-tlc.cjs +467 -0
  125. package/bin/run-transcript-alloy.cjs +173 -0
  126. package/bin/run-uppaal.cjs +264 -0
  127. package/bin/secrets.cjs +134 -0
  128. package/bin/sensitivity-report.cjs +219 -0
  129. package/bin/sensitivity-sweep-feedback.cjs +194 -0
  130. package/bin/set-secret.cjs +29 -0
  131. package/bin/setup-telemetry-cron.sh +36 -0
  132. package/bin/sweepPtoF.cjs +63 -0
  133. package/bin/sync-baseline-requirements.cjs +290 -0
  134. package/bin/task-envelope.cjs +360 -0
  135. package/bin/telemetry-collector.cjs +229 -0
  136. package/bin/unified-mcp-server.mjs +735 -0
  137. package/bin/update-agents.cjs +369 -0
  138. package/bin/update-scoreboard.cjs +1134 -0
  139. package/bin/validate-debt-entry.cjs +207 -0
  140. package/bin/validate-invariant.cjs +419 -0
  141. package/bin/validate-memory.cjs +389 -0
  142. package/bin/validate-requirements-haiku.cjs +435 -0
  143. package/bin/validate-traces.cjs +438 -0
  144. package/bin/verify-formal-results.cjs +124 -0
  145. package/bin/verify-quorum-health.cjs +273 -0
  146. package/bin/write-check-result.cjs +106 -0
  147. package/bin/xstate-to-tla.cjs +483 -0
  148. package/bin/xstate-trace-walker.cjs +205 -0
  149. package/commands/qgsd/add-phase.md +43 -0
  150. package/commands/qgsd/add-requirement.md +24 -0
  151. package/commands/qgsd/add-todo.md +47 -0
  152. package/commands/qgsd/audit-milestone.md +37 -0
  153. package/commands/qgsd/check-todos.md +45 -0
  154. package/commands/qgsd/cleanup.md +18 -0
  155. package/commands/qgsd/close-formal-gaps.md +33 -0
  156. package/commands/qgsd/complete-milestone.md +136 -0
  157. package/commands/qgsd/debug.md +166 -0
  158. package/commands/qgsd/discuss-phase.md +83 -0
  159. package/commands/qgsd/execute-phase.md +117 -0
  160. package/commands/qgsd/fix-tests.md +27 -0
  161. package/commands/qgsd/formal-test-sync.md +32 -0
  162. package/commands/qgsd/health.md +22 -0
  163. package/commands/qgsd/help.md +22 -0
  164. package/commands/qgsd/insert-phase.md +32 -0
  165. package/commands/qgsd/join-discord.md +18 -0
  166. package/commands/qgsd/list-phase-assumptions.md +46 -0
  167. package/commands/qgsd/map-codebase.md +71 -0
  168. package/commands/qgsd/map-requirements.md +20 -0
  169. package/commands/qgsd/mcp-restart.md +176 -0
  170. package/commands/qgsd/mcp-set-model.md +134 -0
  171. package/commands/qgsd/mcp-setup.md +1371 -0
  172. package/commands/qgsd/mcp-status.md +274 -0
  173. package/commands/qgsd/mcp-update.md +238 -0
  174. package/commands/qgsd/new-milestone.md +44 -0
  175. package/commands/qgsd/new-project.md +42 -0
  176. package/commands/qgsd/observe.md +260 -0
  177. package/commands/qgsd/pause-work.md +38 -0
  178. package/commands/qgsd/plan-milestone-gaps.md +34 -0
  179. package/commands/qgsd/plan-phase.md +44 -0
  180. package/commands/qgsd/polyrepo.md +50 -0
  181. package/commands/qgsd/progress.md +24 -0
  182. package/commands/qgsd/queue.md +54 -0
  183. package/commands/qgsd/quick.md +133 -0
  184. package/commands/qgsd/quorum-test.md +275 -0
  185. package/commands/qgsd/quorum.md +707 -0
  186. package/commands/qgsd/reapply-patches.md +110 -0
  187. package/commands/qgsd/remove-phase.md +31 -0
  188. package/commands/qgsd/research-phase.md +189 -0
  189. package/commands/qgsd/resume-work.md +40 -0
  190. package/commands/qgsd/set-profile.md +34 -0
  191. package/commands/qgsd/settings.md +39 -0
  192. package/commands/qgsd/solve.md +565 -0
  193. package/commands/qgsd/sync-baselines.md +119 -0
  194. package/commands/qgsd/triage.md +233 -0
  195. package/commands/qgsd/update.md +37 -0
  196. package/commands/qgsd/verify-work.md +38 -0
  197. package/hooks/dist/config-loader.js +297 -0
  198. package/hooks/dist/conformance-schema.cjs +12 -0
  199. package/hooks/dist/gsd-context-monitor.js +64 -0
  200. package/hooks/dist/qgsd-check-update.js +62 -0
  201. package/hooks/dist/qgsd-circuit-breaker.js +682 -0
  202. package/hooks/dist/qgsd-precompact.js +156 -0
  203. package/hooks/dist/qgsd-prompt.js +653 -0
  204. package/hooks/dist/qgsd-session-start.js +122 -0
  205. package/hooks/dist/qgsd-slot-correlator.js +58 -0
  206. package/hooks/dist/qgsd-spec-regen.js +86 -0
  207. package/hooks/dist/qgsd-statusline.js +91 -0
  208. package/hooks/dist/qgsd-stop.js +553 -0
  209. package/hooks/dist/qgsd-token-collector.js +133 -0
  210. package/hooks/dist/unified-mcp-server.mjs +669 -0
  211. package/package.json +95 -0
  212. package/scripts/build-hooks.js +46 -0
  213. package/scripts/postinstall.js +48 -0
  214. package/scripts/secret-audit.sh +45 -0
  215. package/templates/qgsd.json +49 -0
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * telemetry-collector.cjs
6
+ *
7
+ * Pure disk I/O telemetry collector for QGSD.
8
+ * Reads existing log sources, aggregates stats, writes .planning/telemetry/report.json.
9
+ *
10
+ * Sources:
11
+ * 1. ~/.claude/debug/*.txt — MCP tool call timing and failures
12
+ * 2. .planning/quorum-scoreboard.json — quorum availability stats
13
+ * 3. .claude/circuit-breaker-state.json — circuit breaker state
14
+ *
15
+ * NEVER spawns Claude or calls any MCP tool. Handles all missing files gracefully.
16
+ *
17
+ * Usage:
18
+ * node bin/telemetry-collector.cjs
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+ const os = require('os');
24
+
25
+ const DEBUG_DIR = path.join(os.homedir(), '.claude', 'debug');
26
+ const MAX_FILES = 100;
27
+ const MAX_DAYS = 7;
28
+ const PROJECT_DIR = process.cwd();
29
+ const TELEMETRY_DIR = path.join(PROJECT_DIR, '.planning', 'telemetry');
30
+
31
+ // ─── Regex patterns (reused from review-mcp-logs.cjs) ────────────────────────
32
+ const RE_COMPLETE = /MCP server "([^"]+)": Tool '([^']+)' completed successfully in (\d+)ms/;
33
+ const RE_FAILED = /MCP server "([^"]+)": Tool '([^']+)' failed after (\d+)s: (.+)/;
34
+
35
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
36
+ function percentile(arr, p) {
37
+ if (!arr.length) return 0;
38
+ const sorted = [...arr].sort((a, b) => a - b);
39
+ const idx = Math.ceil((p / 100) * sorted.length) - 1;
40
+ return sorted[Math.max(0, idx)];
41
+ }
42
+
43
+ // ─── Source 1: MCP debug logs ─────────────────────────────────────────────────
44
+ const servers = {}; // serverName -> { totalCalls, successCount, failureCount, durations[], errors[] }
45
+
46
+ function ensureServer(name) {
47
+ if (!servers[name]) {
48
+ servers[name] = { totalCalls: 0, successCount: 0, failureCount: 0, durations: [], errors: [] };
49
+ }
50
+ return servers[name];
51
+ }
52
+
53
+ let mcpResult = {
54
+ servers: {},
55
+ alwaysFailing: [],
56
+ slowServers: [],
57
+ };
58
+
59
+ try {
60
+ const cutoff = Date.now() - MAX_DAYS * 86400 * 1000;
61
+
62
+ let files = [];
63
+ if (fs.existsSync(DEBUG_DIR)) {
64
+ files = fs.readdirSync(DEBUG_DIR)
65
+ .filter(f => f.endsWith('.txt'))
66
+ .map(f => ({ name: f, mtime: fs.statSync(path.join(DEBUG_DIR, f)).mtimeMs }))
67
+ .filter(f => f.mtime >= cutoff)
68
+ .sort((a, b) => b.mtime - a.mtime)
69
+ .slice(0, MAX_FILES)
70
+ .map(f => path.join(DEBUG_DIR, f.name));
71
+ }
72
+
73
+ for (const file of files) {
74
+ let content;
75
+ try { content = fs.readFileSync(file, 'utf8'); } catch { continue; }
76
+
77
+ for (const line of content.split('\n')) {
78
+ let m;
79
+
80
+ if ((m = line.match(RE_COMPLETE))) {
81
+ const [, server, , ms] = m;
82
+ const s = ensureServer(server);
83
+ const durationMs = parseInt(ms, 10);
84
+ s.totalCalls++;
85
+ s.successCount++;
86
+ s.durations.push(durationMs);
87
+ // hang if successful call > 60000ms
88
+ if (durationMs > 60000) {
89
+ // count as hang but still a success
90
+ }
91
+ } else if ((m = line.match(RE_FAILED))) {
92
+ const [, server, , sec, reason] = m;
93
+ const s = ensureServer(server);
94
+ const durationMs = parseInt(sec, 10) * 1000;
95
+ s.totalCalls++;
96
+ s.failureCount++;
97
+ s.errors.push(reason.trim().slice(0, 120));
98
+ if (durationMs > 60000) {
99
+ // hang: counted separately
100
+ }
101
+ }
102
+ }
103
+ }
104
+
105
+ // Build per-server stats
106
+ for (const [name, data] of Object.entries(servers)) {
107
+ // Count hangs: completed calls with duration > 60000ms
108
+ // We stored durations only for successful calls; failed calls with >60s are also hangs
109
+ const hangCount = data.durations.filter(d => d > 60000).length;
110
+
111
+ // Top 3 error reasons by frequency
112
+ const errFreq = {};
113
+ for (const e of data.errors) {
114
+ errFreq[e] = (errFreq[e] || 0) + 1;
115
+ }
116
+ const topErrors = Object.entries(errFreq)
117
+ .sort(([, a], [, b]) => b - a)
118
+ .slice(0, 3)
119
+ .map(([msg]) => msg);
120
+
121
+ const p95Ms = percentile(data.durations, 95);
122
+
123
+ mcpResult.servers[name] = {
124
+ totalCalls: data.totalCalls,
125
+ failureCount: data.failureCount,
126
+ hangCount,
127
+ topErrors,
128
+ p95Ms,
129
+ };
130
+ }
131
+
132
+ // alwaysFailing: servers with totalCalls > 0 and successCount === 0
133
+ mcpResult.alwaysFailing = Object.entries(servers)
134
+ .filter(([, s]) => s.totalCalls > 0 && s.successCount === 0)
135
+ .map(([name]) => name);
136
+
137
+ // slowServers: p95 > 30000ms and at least 1 success
138
+ mcpResult.slowServers = Object.entries(servers)
139
+ .filter(([, s]) => {
140
+ const p95 = percentile(s.durations, 95);
141
+ return p95 > 30000 && s.successCount > 0;
142
+ })
143
+ .map(([name, s]) => ({ name, p95Ms: percentile(s.durations, 95) }))
144
+ .sort((a, b) => b.p95Ms - a.p95Ms);
145
+
146
+ } catch (err) {
147
+ // Non-fatal: leave mcpResult with empty defaults
148
+ process.stderr.write('[telemetry-collector] MCP parse error: ' + err.message + '\n');
149
+ }
150
+
151
+ // ─── Source 2: quorum-scoreboard.json ─────────────────────────────────────────
152
+ let quorumResult = {
153
+ totalRounds: 0,
154
+ allUnavailableRounds: 0,
155
+ quorumFailureRate: 0,
156
+ };
157
+
158
+ try {
159
+ const pp = require('./planning-paths.cjs');
160
+ const scoreboardPath = pp.resolveWithFallback(PROJECT_DIR, 'quorum-scoreboard');
161
+ if (fs.existsSync(scoreboardPath)) {
162
+ const sb = JSON.parse(fs.readFileSync(scoreboardPath, 'utf8'));
163
+ if (Array.isArray(sb.rounds)) {
164
+ const total = sb.rounds.length;
165
+ let allUnavail = 0;
166
+ for (const round of sb.rounds) {
167
+ // A round is "all unavailable" if votes is empty or every model is UNAVAILABLE
168
+ if (!round.votes || Object.keys(round.votes).length === 0) {
169
+ allUnavail++;
170
+ } else {
171
+ const vals = Object.values(round.votes);
172
+ if (vals.length > 0 && vals.every(v => v === 'UNAVAILABLE')) {
173
+ allUnavail++;
174
+ }
175
+ }
176
+ }
177
+ quorumResult.totalRounds = total;
178
+ quorumResult.allUnavailableRounds = allUnavail;
179
+ quorumResult.quorumFailureRate = total > 0 ? allUnavail / total : 0;
180
+ }
181
+ }
182
+ } catch (err) {
183
+ process.stderr.write('[telemetry-collector] Scoreboard parse error: ' + err.message + '\n');
184
+ }
185
+
186
+ // ─── Source 3: circuit-breaker-state.json ─────────────────────────────────────
187
+ let cbResult = {
188
+ active: false,
189
+ triggerCount: 0,
190
+ lastTriggeredAt: null,
191
+ };
192
+
193
+ try {
194
+ const cbPath = path.join(PROJECT_DIR, '.claude', 'circuit-breaker-state.json');
195
+ if (fs.existsSync(cbPath)) {
196
+ const cb = JSON.parse(fs.readFileSync(cbPath, 'utf8'));
197
+ cbResult.active = Boolean(cb.active);
198
+ cbResult.triggerCount = typeof cb.triggerCount === 'number' ? cb.triggerCount : 0;
199
+ cbResult.lastTriggeredAt = cb.lastTriggeredAt || null;
200
+ }
201
+ } catch (err) {
202
+ process.stderr.write('[telemetry-collector] Circuit breaker parse error: ' + err.message + '\n');
203
+ }
204
+
205
+ // ─── Build report ─────────────────────────────────────────────────────────────
206
+ const report = {
207
+ generatedAt: new Date().toISOString(),
208
+ mcp: {
209
+ servers: mcpResult.servers,
210
+ alwaysFailing: mcpResult.alwaysFailing,
211
+ slowServers: mcpResult.slowServers,
212
+ },
213
+ quorum: {
214
+ totalRounds: quorumResult.totalRounds,
215
+ allUnavailableRounds: quorumResult.allUnavailableRounds,
216
+ quorumFailureRate: quorumResult.quorumFailureRate,
217
+ },
218
+ circuitBreaker: {
219
+ active: cbResult.active,
220
+ triggerCount: cbResult.triggerCount,
221
+ lastTriggeredAt: cbResult.lastTriggeredAt,
222
+ },
223
+ };
224
+
225
+ // ─── Write output ─────────────────────────────────────────────────────────────
226
+ fs.mkdirSync(TELEMETRY_DIR, { recursive: true });
227
+ const reportPath = path.join(TELEMETRY_DIR, 'report.json');
228
+ fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
229
+ console.log('[telemetry-collector] Report written to ' + reportPath);