@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.
- package/LICENSE +22 -0
- package/README.md +1024 -0
- package/agents/qgsd-codebase-mapper.md +764 -0
- package/agents/qgsd-debugger.md +1201 -0
- package/agents/qgsd-executor.md +472 -0
- package/agents/qgsd-integration-checker.md +443 -0
- package/agents/qgsd-phase-researcher.md +502 -0
- package/agents/qgsd-plan-checker.md +643 -0
- package/agents/qgsd-planner.md +1182 -0
- package/agents/qgsd-project-researcher.md +621 -0
- package/agents/qgsd-quorum-orchestrator.md +628 -0
- package/agents/qgsd-quorum-slot-worker.md +41 -0
- package/agents/qgsd-quorum-synthesizer.md +133 -0
- package/agents/qgsd-quorum-test-worker.md +37 -0
- package/agents/qgsd-quorum-worker.md +161 -0
- package/agents/qgsd-research-synthesizer.md +239 -0
- package/agents/qgsd-roadmapper.md +660 -0
- package/agents/qgsd-verifier.md +628 -0
- package/bin/accept-debug-invariant.cjs +165 -0
- package/bin/account-manager.cjs +719 -0
- package/bin/aggregate-requirements.cjs +466 -0
- package/bin/analyze-assumptions.cjs +757 -0
- package/bin/analyze-state-space.cjs +921 -0
- package/bin/attribute-trace-divergence.cjs +150 -0
- package/bin/auth-drivers/gh-cli.cjs +93 -0
- package/bin/auth-drivers/index.cjs +46 -0
- package/bin/auth-drivers/pool.cjs +67 -0
- package/bin/auth-drivers/simple.cjs +95 -0
- package/bin/autoClosePtoF.cjs +110 -0
- package/bin/blessed-terminal.cjs +350 -0
- package/bin/build-phase-index.cjs +472 -0
- package/bin/call-quorum-slot.cjs +541 -0
- package/bin/ccr-secure-config.cjs +99 -0
- package/bin/ccr-secure-start.cjs +83 -0
- package/bin/check-bundled-sdks.cjs +177 -0
- package/bin/check-coverage-guard.cjs +112 -0
- package/bin/check-liveness-fairness.cjs +95 -0
- package/bin/check-mcp-health.cjs +123 -0
- package/bin/check-provider-health.cjs +395 -0
- package/bin/check-results-exit.cjs +24 -0
- package/bin/check-spec-sync.cjs +360 -0
- package/bin/check-trace-redaction.cjs +271 -0
- package/bin/check-trace-schema-drift.cjs +99 -0
- package/bin/compareDrift.cjs +21 -0
- package/bin/conformance-schema.cjs +12 -0
- package/bin/count-scenarios.cjs +420 -0
- package/bin/debt-dedup.cjs +144 -0
- package/bin/debt-ledger.cjs +61 -0
- package/bin/debt-retention.cjs +76 -0
- package/bin/debt-state-machine.cjs +80 -0
- package/bin/detect-coverage-gaps.cjs +204 -0
- package/bin/detect-project-intent.cjs +362 -0
- package/bin/export-prism-constants.cjs +164 -0
- package/bin/extract-annotations.cjs +633 -0
- package/bin/extractFormalExpected.cjs +104 -0
- package/bin/fingerprint-drift.cjs +24 -0
- package/bin/fingerprint-issue.cjs +46 -0
- package/bin/formal-core.cjs +519 -0
- package/bin/formal-ref-linker.cjs +141 -0
- package/bin/formal-test-sync.cjs +788 -0
- package/bin/generate-formal-specs.cjs +588 -0
- package/bin/generate-petri-net.cjs +397 -0
- package/bin/generate-phase-spec.cjs +249 -0
- package/bin/generate-proposed-changes.cjs +194 -0
- package/bin/generate-tla-cfg.cjs +122 -0
- package/bin/generate-traceability-matrix.cjs +701 -0
- package/bin/generate-triage-bundle.cjs +300 -0
- package/bin/gh-account-rotate.cjs +34 -0
- package/bin/initialize-model-registry.cjs +105 -0
- package/bin/install-formal-tools.cjs +382 -0
- package/bin/install.js +2424 -0
- package/bin/isNumericThreshold.cjs +34 -0
- package/bin/issue-classifier.cjs +151 -0
- package/bin/levenshtein.cjs +74 -0
- package/bin/lint-formal-models.cjs +580 -0
- package/bin/load-baseline-requirements.cjs +275 -0
- package/bin/manage-agents-core.cjs +815 -0
- package/bin/migrate-formal-dir.cjs +172 -0
- package/bin/migrate-planning.cjs +206 -0
- package/bin/migrate-to-slots.cjs +255 -0
- package/bin/nForma.cjs +2726 -0
- package/bin/observe-config.cjs +353 -0
- package/bin/observe-debt-writer.cjs +140 -0
- package/bin/observe-handler-grafana.cjs +128 -0
- package/bin/observe-handler-internal.cjs +301 -0
- package/bin/observe-handler-logstash.cjs +153 -0
- package/bin/observe-handler-prometheus.cjs +185 -0
- package/bin/observe-handlers.cjs +436 -0
- package/bin/observe-registry.cjs +131 -0
- package/bin/observe-render.cjs +168 -0
- package/bin/planning-paths.cjs +167 -0
- package/bin/polyrepo.cjs +560 -0
- package/bin/prism-priority.cjs +153 -0
- package/bin/probe-quorum-slots.cjs +167 -0
- package/bin/promote-model.cjs +225 -0
- package/bin/propose-debug-invariants.cjs +165 -0
- package/bin/providers.json +392 -0
- package/bin/pty-proxy.py +129 -0
- package/bin/qgsd-solve.cjs +2477 -0
- package/bin/quorum-consensus-gate.cjs +238 -0
- package/bin/quorum-formal-context.cjs +183 -0
- package/bin/quorum-slot-dispatch.cjs +934 -0
- package/bin/read-policy.cjs +60 -0
- package/bin/requirement-map.cjs +63 -0
- package/bin/requirements-core.cjs +247 -0
- package/bin/resolve-cli.cjs +101 -0
- package/bin/review-mcp-logs.cjs +294 -0
- package/bin/run-account-manager-tlc.cjs +188 -0
- package/bin/run-account-pool-alloy.cjs +158 -0
- package/bin/run-alloy.cjs +153 -0
- package/bin/run-audit-alloy.cjs +187 -0
- package/bin/run-breaker-tlc.cjs +181 -0
- package/bin/run-formal-check.cjs +395 -0
- package/bin/run-formal-verify.cjs +701 -0
- package/bin/run-installer-alloy.cjs +188 -0
- package/bin/run-oauth-rotation-prism.cjs +132 -0
- package/bin/run-oscillation-tlc.cjs +202 -0
- package/bin/run-phase-tlc.cjs +228 -0
- package/bin/run-prism.cjs +446 -0
- package/bin/run-protocol-tlc.cjs +201 -0
- package/bin/run-quorum-composition-alloy.cjs +155 -0
- package/bin/run-sensitivity-sweep.cjs +231 -0
- package/bin/run-stop-hook-tlc.cjs +188 -0
- package/bin/run-tlc.cjs +467 -0
- package/bin/run-transcript-alloy.cjs +173 -0
- package/bin/run-uppaal.cjs +264 -0
- package/bin/secrets.cjs +134 -0
- package/bin/sensitivity-report.cjs +219 -0
- package/bin/sensitivity-sweep-feedback.cjs +194 -0
- package/bin/set-secret.cjs +29 -0
- package/bin/setup-telemetry-cron.sh +36 -0
- package/bin/sweepPtoF.cjs +63 -0
- package/bin/sync-baseline-requirements.cjs +290 -0
- package/bin/task-envelope.cjs +360 -0
- package/bin/telemetry-collector.cjs +229 -0
- package/bin/unified-mcp-server.mjs +735 -0
- package/bin/update-agents.cjs +369 -0
- package/bin/update-scoreboard.cjs +1134 -0
- package/bin/validate-debt-entry.cjs +207 -0
- package/bin/validate-invariant.cjs +419 -0
- package/bin/validate-memory.cjs +389 -0
- package/bin/validate-requirements-haiku.cjs +435 -0
- package/bin/validate-traces.cjs +438 -0
- package/bin/verify-formal-results.cjs +124 -0
- package/bin/verify-quorum-health.cjs +273 -0
- package/bin/write-check-result.cjs +106 -0
- package/bin/xstate-to-tla.cjs +483 -0
- package/bin/xstate-trace-walker.cjs +205 -0
- package/commands/qgsd/add-phase.md +43 -0
- package/commands/qgsd/add-requirement.md +24 -0
- package/commands/qgsd/add-todo.md +47 -0
- package/commands/qgsd/audit-milestone.md +37 -0
- package/commands/qgsd/check-todos.md +45 -0
- package/commands/qgsd/cleanup.md +18 -0
- package/commands/qgsd/close-formal-gaps.md +33 -0
- package/commands/qgsd/complete-milestone.md +136 -0
- package/commands/qgsd/debug.md +166 -0
- package/commands/qgsd/discuss-phase.md +83 -0
- package/commands/qgsd/execute-phase.md +117 -0
- package/commands/qgsd/fix-tests.md +27 -0
- package/commands/qgsd/formal-test-sync.md +32 -0
- package/commands/qgsd/health.md +22 -0
- package/commands/qgsd/help.md +22 -0
- package/commands/qgsd/insert-phase.md +32 -0
- package/commands/qgsd/join-discord.md +18 -0
- package/commands/qgsd/list-phase-assumptions.md +46 -0
- package/commands/qgsd/map-codebase.md +71 -0
- package/commands/qgsd/map-requirements.md +20 -0
- package/commands/qgsd/mcp-restart.md +176 -0
- package/commands/qgsd/mcp-set-model.md +134 -0
- package/commands/qgsd/mcp-setup.md +1371 -0
- package/commands/qgsd/mcp-status.md +274 -0
- package/commands/qgsd/mcp-update.md +238 -0
- package/commands/qgsd/new-milestone.md +44 -0
- package/commands/qgsd/new-project.md +42 -0
- package/commands/qgsd/observe.md +260 -0
- package/commands/qgsd/pause-work.md +38 -0
- package/commands/qgsd/plan-milestone-gaps.md +34 -0
- package/commands/qgsd/plan-phase.md +44 -0
- package/commands/qgsd/polyrepo.md +50 -0
- package/commands/qgsd/progress.md +24 -0
- package/commands/qgsd/queue.md +54 -0
- package/commands/qgsd/quick.md +133 -0
- package/commands/qgsd/quorum-test.md +275 -0
- package/commands/qgsd/quorum.md +707 -0
- package/commands/qgsd/reapply-patches.md +110 -0
- package/commands/qgsd/remove-phase.md +31 -0
- package/commands/qgsd/research-phase.md +189 -0
- package/commands/qgsd/resume-work.md +40 -0
- package/commands/qgsd/set-profile.md +34 -0
- package/commands/qgsd/settings.md +39 -0
- package/commands/qgsd/solve.md +565 -0
- package/commands/qgsd/sync-baselines.md +119 -0
- package/commands/qgsd/triage.md +233 -0
- package/commands/qgsd/update.md +37 -0
- package/commands/qgsd/verify-work.md +38 -0
- package/hooks/dist/config-loader.js +297 -0
- package/hooks/dist/conformance-schema.cjs +12 -0
- package/hooks/dist/gsd-context-monitor.js +64 -0
- package/hooks/dist/qgsd-check-update.js +62 -0
- package/hooks/dist/qgsd-circuit-breaker.js +682 -0
- package/hooks/dist/qgsd-precompact.js +156 -0
- package/hooks/dist/qgsd-prompt.js +653 -0
- package/hooks/dist/qgsd-session-start.js +122 -0
- package/hooks/dist/qgsd-slot-correlator.js +58 -0
- package/hooks/dist/qgsd-spec-regen.js +86 -0
- package/hooks/dist/qgsd-statusline.js +91 -0
- package/hooks/dist/qgsd-stop.js +553 -0
- package/hooks/dist/qgsd-token-collector.js +133 -0
- package/hooks/dist/unified-mcp-server.mjs +669 -0
- package/package.json +95 -0
- package/scripts/build-hooks.js +46 -0
- package/scripts/postinstall.js +48 -0
- package/scripts/secret-audit.sh +45 -0
- package/templates/qgsd.json +49 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dual-table renderer for /qgsd:observe
|
|
3
|
+
* Renders Issues table and Drifts table with error section
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Severity sort order (lower = higher priority)
|
|
7
|
+
const SEVERITY_ORDER = { error: 0, critical: 0, bug: 1, warning: 2, info: 3 };
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get sort order for a severity string
|
|
11
|
+
* @param {string} severity
|
|
12
|
+
* @returns {number}
|
|
13
|
+
*/
|
|
14
|
+
function classifySeverity(severity) {
|
|
15
|
+
return SEVERITY_ORDER[severity] ?? 4;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Format age from ISO date to human-readable string
|
|
20
|
+
* @param {string} isoDate
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
function formatAge(isoDate) {
|
|
24
|
+
if (!isoDate) return '';
|
|
25
|
+
const diffMs = Date.now() - new Date(isoDate).getTime();
|
|
26
|
+
if (diffMs < 0) return 'future';
|
|
27
|
+
const minutes = Math.floor(diffMs / 60000);
|
|
28
|
+
if (minutes < 60) return `${minutes}m`;
|
|
29
|
+
const hours = Math.floor(minutes / 60);
|
|
30
|
+
if (hours < 24) return `${hours}h`;
|
|
31
|
+
const days = Math.floor(hours / 24);
|
|
32
|
+
return `${days}d`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Truncate a string to maxLen, adding "..." if truncated
|
|
37
|
+
* @param {string} str
|
|
38
|
+
* @param {number} maxLen
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
function truncate(str, maxLen) {
|
|
42
|
+
if (!str) return '';
|
|
43
|
+
if (str.length <= maxLen) return str;
|
|
44
|
+
return str.slice(0, maxLen - 3) + '...';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Pad or truncate string to exact width
|
|
49
|
+
* @param {string} str
|
|
50
|
+
* @param {number} width
|
|
51
|
+
* @returns {string}
|
|
52
|
+
*/
|
|
53
|
+
function pad(str, width) {
|
|
54
|
+
const s = String(str || '');
|
|
55
|
+
if (s.length >= width) return s.slice(0, width);
|
|
56
|
+
return s + ' '.repeat(width - s.length);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Render observe output with dual tables (Issues + Drifts) and error section
|
|
61
|
+
*
|
|
62
|
+
* @param {object[]} results - Array of handler results (standard schema, all resolved)
|
|
63
|
+
* @returns {string} Formatted output string
|
|
64
|
+
*/
|
|
65
|
+
function renderObserveOutput(results) {
|
|
66
|
+
const lines = [];
|
|
67
|
+
|
|
68
|
+
// Separate successes and errors
|
|
69
|
+
const successes = results.filter(r => r.status === 'ok');
|
|
70
|
+
const errorResults = results.filter(r => r.status === 'error');
|
|
71
|
+
const sourceCount = results.length;
|
|
72
|
+
|
|
73
|
+
// Collect all issues
|
|
74
|
+
const allItems = [];
|
|
75
|
+
for (const r of successes) {
|
|
76
|
+
for (const issue of (r.issues || [])) {
|
|
77
|
+
allItems.push({ ...issue, source_label: issue.source_label || r.source_label });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Split by issue_type
|
|
82
|
+
const issues = allItems.filter(item => item.issue_type !== 'drift');
|
|
83
|
+
const drifts = allItems.filter(item => item.issue_type === 'drift');
|
|
84
|
+
|
|
85
|
+
const totalIssues = issues.length;
|
|
86
|
+
const totalDrifts = drifts.length;
|
|
87
|
+
|
|
88
|
+
// Header
|
|
89
|
+
if (totalIssues === 0 && totalDrifts === 0 && errorResults.length === 0) {
|
|
90
|
+
lines.push('');
|
|
91
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
92
|
+
lines.push(' QGSD > OBSERVE: All clear — no open issues found');
|
|
93
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
94
|
+
lines.push(`Sources checked: ${sourceCount}`);
|
|
95
|
+
return lines.join('\n');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const failNote = errorResults.length > 0 ? `; ${errorResults.length} source(s) failed` : '';
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
101
|
+
lines.push(` QGSD > OBSERVE: ${totalIssues} issue(s), ${totalDrifts} drift(s) across ${sourceCount} source(s)${failNote}`);
|
|
102
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
103
|
+
|
|
104
|
+
// Sort issues by severity then age (newest first)
|
|
105
|
+
issues.sort((a, b) => {
|
|
106
|
+
const sevCmp = classifySeverity(a.severity) - classifySeverity(b.severity);
|
|
107
|
+
if (sevCmp !== 0) return sevCmp;
|
|
108
|
+
return new Date(b.created_at || 0) - new Date(a.created_at || 0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Sort drifts by severity
|
|
112
|
+
drifts.sort((a, b) => {
|
|
113
|
+
return classifySeverity(a.severity) - classifySeverity(b.severity);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Render Issues table
|
|
117
|
+
if (issues.length > 0) {
|
|
118
|
+
lines.push('');
|
|
119
|
+
lines.push('┌────────────────────────────── ISSUES ───────────────────────────────┐');
|
|
120
|
+
lines.push('│ # │ Title │ Source │ Sev │ Age │');
|
|
121
|
+
lines.push('├─────────────────────────────────────────────────────────────────────┤');
|
|
122
|
+
|
|
123
|
+
for (let i = 0; i < issues.length; i++) {
|
|
124
|
+
const issue = issues[i];
|
|
125
|
+
const num = String(i + 1).padStart(2, ' ');
|
|
126
|
+
const title = pad(truncate(issue.title, 40), 40);
|
|
127
|
+
const source = pad(truncate(issue.source_label || issue.source_type || '', 7), 7);
|
|
128
|
+
const sev = pad(truncate(issue.severity || 'info', 3), 3);
|
|
129
|
+
const age = pad(issue.age || formatAge(issue.created_at), 4);
|
|
130
|
+
lines.push(`│ ${num} │ ${title} │ ${source} │ ${sev} │ ${age} │`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
lines.push('└─────────────────────────────────────────────────────────────────────┘');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Render Drifts table
|
|
137
|
+
if (drifts.length > 0) {
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push('┌────────────────────────────── DRIFTS ───────────────────────────────┐');
|
|
140
|
+
lines.push('│ # │ Parameter │ Formal │ Actual │ Sev │');
|
|
141
|
+
lines.push('├─────────────────────────────────────────────────────────────────────┤');
|
|
142
|
+
|
|
143
|
+
for (let i = 0; i < drifts.length; i++) {
|
|
144
|
+
const drift = drifts[i];
|
|
145
|
+
const num = String(i + 1).padStart(2, ' ');
|
|
146
|
+
const param = pad(truncate(drift.formal_parameter_key || drift.title || '', 36), 36);
|
|
147
|
+
const formal = pad(truncate(String(drift.formal_value || ''), 6), 6);
|
|
148
|
+
const actual = pad(truncate(String(drift.actual_value || ''), 6), 6);
|
|
149
|
+
const sev = pad(truncate(drift.severity || 'info', 4), 4);
|
|
150
|
+
lines.push(`│ ${num} │ ${param} │ ${formal} │ ${actual} │ ${sev} │`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
lines.push('└─────────────────────────────────────────────────────────────────────┘');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Render errors section
|
|
157
|
+
if (errorResults.length > 0) {
|
|
158
|
+
lines.push('');
|
|
159
|
+
lines.push(`Errors from sources (${errorResults.length} failed, did not block others):`);
|
|
160
|
+
for (const err of errorResults) {
|
|
161
|
+
lines.push(` ✗ ${err.source_label || err.source_type}: ${err.error || 'Unknown error'}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return lines.join('\n');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = { renderObserveOutput, classifySeverity, formatAge, truncate };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* planning-paths.cjs — Centralized path resolver for .planning/ hierarchy.
|
|
6
|
+
*
|
|
7
|
+
* New layout (v0.27+):
|
|
8
|
+
* .planning/quorum/rounds/ quorum-rounds-session-*.jsonl
|
|
9
|
+
* .planning/quorum/correlations/ quorum-slot-corr-*.json
|
|
10
|
+
* .planning/telemetry/ conformance-events.jsonl, token-usage.jsonl
|
|
11
|
+
* .planning/milestones/ v*-MILESTONE-AUDIT.md, v*-INTEGRATION-*.{md,txt}
|
|
12
|
+
* .planning/archive/state-backups/ STATE.md.bak-*
|
|
13
|
+
* .planning/archive/designs/ dated docs, old roadmaps
|
|
14
|
+
*
|
|
15
|
+
* Legacy layout (pre-v0.27):
|
|
16
|
+
* All above files lived directly in .planning/
|
|
17
|
+
*
|
|
18
|
+
* API:
|
|
19
|
+
* resolve(root, type, params) → canonical (new) path
|
|
20
|
+
* resolveWithFallback(root, type, params) → new path if exists, else legacy path
|
|
21
|
+
* legacy(root, type, params) → legacy flat path
|
|
22
|
+
* needsMigration(root) → boolean
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
|
|
28
|
+
// ─── Path definitions ────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
const TYPES = {
|
|
31
|
+
// Quorum runtime (high-volume ephemeral)
|
|
32
|
+
'quorum-rounds': {
|
|
33
|
+
canonical: (root, p) => path.join(root, '.planning', 'quorum', 'rounds', `quorum-rounds-${p.sessionId}.jsonl`),
|
|
34
|
+
legacy: (root, p) => path.join(root, '.planning', `quorum-rounds-${p.sessionId}.jsonl`),
|
|
35
|
+
},
|
|
36
|
+
'quorum-correlation': {
|
|
37
|
+
canonical: (root, p) => path.join(root, '.planning', 'quorum', 'correlations', `quorum-slot-corr-${p.agentId}.json`),
|
|
38
|
+
legacy: (root, p) => path.join(root, '.planning', `quorum-slot-corr-${p.agentId}.json`),
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Quorum aggregate data
|
|
42
|
+
'quorum-scoreboard': {
|
|
43
|
+
canonical: (root) => path.join(root, '.planning', 'quorum', 'scoreboard.json'),
|
|
44
|
+
legacy: (root) => path.join(root, '.planning', 'quorum-scoreboard.json'),
|
|
45
|
+
},
|
|
46
|
+
'quorum-failures': {
|
|
47
|
+
canonical: (root) => path.join(root, '.planning', 'quorum', 'failures.json'),
|
|
48
|
+
legacy: (root) => path.join(root, '.planning', 'quorum-failures.json'),
|
|
49
|
+
},
|
|
50
|
+
'quorum-debate': {
|
|
51
|
+
canonical: (root, p) => path.join(root, '.planning', 'quorum', 'debates', p.filename),
|
|
52
|
+
legacy: (root, p) => path.join(root, '.planning', 'debates', p.filename),
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
// Telemetry
|
|
56
|
+
'conformance-events': {
|
|
57
|
+
canonical: (root) => path.join(root, '.planning', 'telemetry', 'conformance-events.jsonl'),
|
|
58
|
+
legacy: (root) => path.join(root, '.planning', 'conformance-events.jsonl'),
|
|
59
|
+
},
|
|
60
|
+
'token-usage': {
|
|
61
|
+
canonical: (root) => path.join(root, '.planning', 'telemetry', 'token-usage.jsonl'),
|
|
62
|
+
legacy: (root) => path.join(root, '.planning', 'token-usage.jsonl'),
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Milestone artifacts (loose ones at root → milestones/)
|
|
66
|
+
'milestone-audit': {
|
|
67
|
+
canonical: (root, p) => path.join(root, '.planning', 'milestones', `${p.version}-MILESTONE-AUDIT.md`),
|
|
68
|
+
legacy: (root, p) => path.join(root, '.planning', `${p.version}-MILESTONE-AUDIT.md`),
|
|
69
|
+
},
|
|
70
|
+
'integration-check': {
|
|
71
|
+
canonical: (root, p) => path.join(root, '.planning', 'milestones', `${p.version}-INTEGRATION-CHECK.md`),
|
|
72
|
+
legacy: (root, p) => path.join(root, '.planning', `${p.version}-INTEGRATION-CHECK.md`),
|
|
73
|
+
},
|
|
74
|
+
'integration-summary': {
|
|
75
|
+
canonical: (root, p) => path.join(root, '.planning', 'milestones', `${p.version}-INTEGRATION-SUMMARY.txt`),
|
|
76
|
+
legacy: (root, p) => path.join(root, '.planning', `${p.version}-INTEGRATION-SUMMARY.txt`),
|
|
77
|
+
},
|
|
78
|
+
'integration-key-files': {
|
|
79
|
+
canonical: (root, p) => path.join(root, '.planning', 'milestones', `${p.version}-INTEGRATION-KEY-FILES.md`),
|
|
80
|
+
legacy: (root, p) => path.join(root, '.planning', `${p.version}-INTEGRATION-KEY-FILES.md`),
|
|
81
|
+
},
|
|
82
|
+
'integration-report': {
|
|
83
|
+
canonical: (root, p) => path.join(root, '.planning', 'milestones', `${p.version}-INTEGRATION-REPORT.md`),
|
|
84
|
+
legacy: (root, p) => path.join(root, '.planning', `${p.version}-INTEGRATION-REPORT.md`),
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// State backups
|
|
88
|
+
'state-backup': {
|
|
89
|
+
canonical: (root, p) => path.join(root, '.planning', 'archive', 'state-backups', `STATE.md.bak-${p.timestamp}`),
|
|
90
|
+
legacy: (root, p) => path.join(root, '.planning', `STATE.md.bak-${p.timestamp}`),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns the canonical (new) path for a given type.
|
|
98
|
+
* Ensures parent directory exists.
|
|
99
|
+
*/
|
|
100
|
+
function resolve(root, type, params) {
|
|
101
|
+
const def = TYPES[type];
|
|
102
|
+
if (!def) throw new Error(`Unknown planning path type: ${type}`);
|
|
103
|
+
const p = def.canonical(root, params || {});
|
|
104
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
105
|
+
return p;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Returns the canonical path if the file exists there,
|
|
110
|
+
* otherwise falls back to the legacy path.
|
|
111
|
+
* Does NOT create directories — use for readers only.
|
|
112
|
+
*/
|
|
113
|
+
function resolveWithFallback(root, type, params) {
|
|
114
|
+
const def = TYPES[type];
|
|
115
|
+
if (!def) throw new Error(`Unknown planning path type: ${type}`);
|
|
116
|
+
const canonical = def.canonical(root, params || {});
|
|
117
|
+
if (fs.existsSync(canonical)) return canonical;
|
|
118
|
+
const leg = def.legacy(root, params || {});
|
|
119
|
+
if (fs.existsSync(leg)) return leg;
|
|
120
|
+
// Neither exists — return canonical (caller will handle missing file)
|
|
121
|
+
return canonical;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Returns the legacy (flat) path.
|
|
126
|
+
*/
|
|
127
|
+
function legacy(root, type, params) {
|
|
128
|
+
const def = TYPES[type];
|
|
129
|
+
if (!def) throw new Error(`Unknown planning path type: ${type}`);
|
|
130
|
+
return def.legacy(root, params || {});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Detect if the .planning/ directory has legacy flat layout.
|
|
135
|
+
* Returns true if any quorum-rounds-session or quorum-slot-corr files
|
|
136
|
+
* exist at the root level.
|
|
137
|
+
*/
|
|
138
|
+
function needsMigration(root) {
|
|
139
|
+
const planDir = path.join(root, '.planning');
|
|
140
|
+
if (!fs.existsSync(planDir)) return false;
|
|
141
|
+
try {
|
|
142
|
+
const entries = fs.readdirSync(planDir);
|
|
143
|
+
return entries.some(e =>
|
|
144
|
+
e.startsWith('quorum-rounds-session-') ||
|
|
145
|
+
e.startsWith('quorum-slot-corr-') ||
|
|
146
|
+
e === 'conformance-events.jsonl' ||
|
|
147
|
+
e === 'token-usage.jsonl' ||
|
|
148
|
+
e === 'quorum-scoreboard.json' ||
|
|
149
|
+
e === 'quorum-failures.json' ||
|
|
150
|
+
e === 'debates' ||
|
|
151
|
+
/^v[\d.]+-MILESTONE-AUDIT\.md$/.test(e) ||
|
|
152
|
+
/^v[\d.]+-INTEGRATION-/.test(e) ||
|
|
153
|
+
/^STATE\.md\.bak-/.test(e)
|
|
154
|
+
);
|
|
155
|
+
} catch (_) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Returns all known type names.
|
|
162
|
+
*/
|
|
163
|
+
function types() {
|
|
164
|
+
return Object.keys(TYPES);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = { resolve, resolveWithFallback, legacy, needsMigration, types, TYPES };
|