Phase 7B, 7E, 7D: Contracts, Diagrams, Sysdoc

This commit is contained in:
Jarvis Prime
2026-03-09 14:42:15 +00:00
parent 4c212740a2
commit d9fd7e3284
6 changed files with 781 additions and 0 deletions

146
sysdoc.js Normal file
View File

@@ -0,0 +1,146 @@
const fs = require('fs');
const path = require('path');
const GraphStore = require('./graph.js');
const { buildSubsystems } = require('./subsystem.js');
const { extractAllContracts } = require('./contracts.js');
const { buildFlowIndex, traceFlow } = require('./flow.js');
const { generateDependencyDiagram, generateFlowDiagram, generateContractDiagram } = require('./diagrams.js');
/**
* Phase 7D: Hierarchical Doc Generator
* Orchestrates 7A, 7B, 7C, and 7E to generate a Divio-structured documentation site.
*/
function generateDocs(graph, srcRoot, outDir, opts = {}) {
const entryPoints = opts.entryPoints || [];
// 1. Build Subsystems (7A)
const subs = buildSubsystems(graph, {
srcDir: opts.srcDir || '/src/',
minTraffic: opts.minTraffic || 3,
crossCuttingThreshold: opts.crossCuttingThreshold || 0.6
});
// 2. Extract Contracts (7B)
const contractsResult = extractAllContracts(subs, srcRoot);
// 3. Trace Flows (7C)
const flowIndex = buildFlowIndex(graph, subs);
const flowResults = entryPoints.map(ep => traceFlow(ep, flowIndex));
// Initialize output directory structure (Divio)
const dirs = [
'reference/subsystems',
'reference/contracts',
'reference/modules',
'explanation',
'tutorials',
'how-to',
'diagrams'
];
for (const d of dirs) {
fs.mkdirSync(path.join(outDir, d), { recursive: true });
}
// Generate Reference: System Architecture
const sysArchPath = path.join(outDir, 'reference/system-architecture.md');
const depDiag = generateDependencyDiagram(subs);
const depDiagPath = 'diagrams/system-deps.mmd';
fs.writeFileSync(path.join(outDir, depDiagPath), depDiag);
const sysArchContent = `# System Architecture
## Subsystems
${subs.subsystems.map(s => `- **${s.name}** (${s.kind}): ${s.entities.modules} modules, ${s.entities.functions} functions`).join('\n')}
## Cross-Cutting Concerns
${subs.crossCutting.map(c => `- **${c}**`).join('\n')}
## Dependency Map
\`\`\`mermaid
${depDiag}
\`\`\`
`;
fs.writeFileSync(sysArchPath, sysArchContent);
// Generate Reference: Subsystem Docs
for (const sub of subs.subsystems) {
const subDocPath = path.join(outDir, `reference/subsystems/${sub.name}.md`);
const subContracts = contractsResult.bySubsystem[sub.name] || [];
let contractSection = '';
if (subContracts.length > 0) {
const contractDiag = generateContractDiagram(subContracts);
fs.writeFileSync(path.join(outDir, `diagrams/${sub.name}-contracts.mmd`), contractDiag);
contractSection = `\n## Contracts\n\`\`\`mermaid\n${contractDiag}\n\`\`\`\n`;
}
const subContent = `# Subsystem: ${sub.name}
**Kind:** ${sub.kind}
**Files:** ${sub.files.length}
## Public Exports
${sub.publicExports.length > 0 ? sub.publicExports.map(e => `- \`${e}\``).join('\n') : '*None*'}
${contractSection}
## Files
${sub.files.map(f => `- \`${f}\``).join('\n')}
`;
fs.writeFileSync(subDocPath, subContent);
}
// Generate Reference: Contracts
const contractDocPath = path.join(outDir, 'reference/contracts/index.md');
const allContractsDiag = generateContractDiagram(contractsResult.contracts);
fs.writeFileSync(path.join(outDir, 'diagrams/all-contracts.mmd'), allContractsDiag);
fs.writeFileSync(contractDocPath, `# System Contracts\n\n\`\`\`mermaid\n${allContractsDiag}\n\`\`\`\n`);
// Generate Explanation: Data Flows
const flowsPath = path.join(outDir, 'explanation/data-flows.md');
let flowsContent = '# Data Flows\n\n';
for (let i = 0; i < flowResults.length; i++) {
const fr = flowResults[i];
if (fr.error) {
flowsContent += `## Flow: ${fr.entryPoint}\n**Error:** ${fr.error}\n\n`;
continue;
}
const flowDiag = generateFlowDiagram(fr);
const diagName = `flow-${i}.mmd`;
fs.writeFileSync(path.join(outDir, `diagrams/${diagName}`), flowDiag);
flowsContent += `## Flow: ${fr.entryPoint}\n`;
flowsContent += `**Subsystem Sequence:** ${fr.subsystemSequence.join(' → ')}\n\n`;
flowsContent += `\`\`\`mermaid\n${flowDiag}\n\`\`\`\n\n`;
}
fs.writeFileSync(flowsPath, flowsContent);
return {
subsystems: subs.subsystems.length,
contracts: contractsResult.contracts.length,
flows: flowResults.length,
outDir
};
}
if (require.main === module) {
const snapshotPath = process.argv[2];
const srcRoot = process.argv[3];
const outDir = process.argv[4];
const entryPoints = process.argv.slice(5);
if (!snapshotPath || !srcRoot || !outDir) {
console.error('Usage: node sysdoc.js <snapshot.json> <srcRoot> <outDir> [entryPoint1] [entryPoint2] ...');
process.exit(1);
}
const graph = GraphStore.loadSnapshot(snapshotPath);
const result = generateDocs(graph, srcRoot, outDir, { entryPoints });
console.log(`Generated docs in ${result.outDir}`);
console.log(`- ${result.subsystems} subsystems`);
console.log(`- ${result.contracts} contracts`);
console.log(`- ${result.flows} flows`);
}
module.exports = { generateDocs };