Phase 6+7D: Sonnet prose generation integration

This commit is contained in:
Jarvis Prime
2026-03-09 18:44:19 +00:00
parent 1869fcb5b2
commit d19cee36d7
2 changed files with 224 additions and 12 deletions

View File

@@ -2,7 +2,7 @@ const fs = require('fs');
const path = require('path');
const GraphStore = require('./graph.js');
const { buildSubsystems } = require('./subsystem.js');
const { extractAllContracts } = require('./contracts.js');
const { extractAllContracts, buildContractXref } = require('./contracts.js');
const { buildFlowIndex, traceFlow } = require('./flow.js');
const { generateDependencyDiagram, generateFlowDiagram, generateContractDiagram } = require('./diagrams.js');
@@ -11,8 +11,20 @@ const { generateDependencyDiagram, generateFlowDiagram, generateContractDiagram
* Orchestrates 7A, 7B, 7C, and 7E to generate a Divio-structured documentation site.
*/
function generateDocs(graph, srcRoot, outDir, opts = {}) {
async function generateDocs(graph, srcRoot, outDir, opts = {}) {
const entryPoints = opts.entryPoints || [];
const useProse = opts.prose === true;
// Optional LLM module for prose enrichment
let proseMod = null;
if (useProse) {
try {
proseMod = require('./prose.js');
console.log('Prose generation enabled (LLM pass active)');
} catch (err) {
console.warn('Prose generation requested but prose.js not available. Skipping LLM pass.');
}
}
// 1. Build Subsystems (7A)
const subs = buildSubsystems(graph, {
@@ -23,6 +35,7 @@ function generateDocs(graph, srcRoot, outDir, opts = {}) {
// 2. Extract Contracts (7B)
const contractsResult = extractAllContracts(subs, srcRoot);
const xref = buildContractXref(contractsResult.contracts, graph, (p) => p.replace(/^\/?src\//, ''));
// 3. Trace Flows (7C)
const flowIndex = buildFlowIndex(graph, subs);
@@ -49,8 +62,15 @@ function generateDocs(graph, srcRoot, outDir, opts = {}) {
const depDiagPath = 'diagrams/system-deps.mmd';
fs.writeFileSync(path.join(outDir, depDiagPath), depDiag);
let archProse = '';
if (proseMod) {
console.log('Generating architecture overview...');
archProse = await proseMod.describeArchitecture(subs.subsystems, subs.crossCutting, {}, {});
archProse = `\n${archProse.trim()}\n\n`;
}
const sysArchContent = `# System Architecture
${archProse}
## Subsystems
${subs.subsystems.map(s => `- **${s.name}** (${s.kind}): ${s.entities.modules} modules, ${s.entities.functions} functions`).join('\n')}
@@ -76,8 +96,15 @@ ${depDiag}
contractSection = `\n## Contracts\n\`\`\`mermaid\n${contractDiag}\n\`\`\`\n`;
}
const subContent = `# Subsystem: ${sub.name}
let subProse = '';
if (proseMod) {
console.log(`Generating prose for subsystem: ${sub.name}...`);
subProse = await proseMod.describeSubsystem(sub, subs.dependencyMatrix, {});
subProse = `\n${subProse.trim()}\n\n`;
}
const subContent = `# Subsystem: ${sub.name}
${subProse}
**Kind:** ${sub.kind}
**Files:** ${sub.files.length}
@@ -94,7 +121,22 @@ ${sub.files.map(f => `- \`${f}\``).join('\n')}
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`);
let contractProseList = '';
if (proseMod && contractsResult.contracts.length > 0) {
console.log(`Generating prose for ${contractsResult.contracts.length} contracts...`);
// Batch processing to avoid overloading the API
const batchSize = 10;
const contractDocs = [];
for (let i = 0; i < contractsResult.contracts.length; i += batchSize) {
const batch = contractsResult.contracts.slice(i, i + batchSize);
const docs = await Promise.all(batch.map(c => proseMod.describeContract(c, xref, {})));
contractDocs.push(...docs);
}
contractProseList = contractsResult.contracts.map((c, i) => `### ${c.name}\n${contractDocs[i].trim()}\n`).join('\n');
}
fs.writeFileSync(contractDocPath, `# System Contracts\n\n\`\`\`mermaid\n${allContractsDiag}\n\`\`\`\n\n${contractProseList}`);
// Generate Explanation: Data Flows
const flowsPath = path.join(outDir, 'explanation/data-flows.md');
@@ -110,7 +152,15 @@ ${sub.files.map(f => `- \`${f}\``).join('\n')}
const diagName = `flow-${i}.mmd`;
fs.writeFileSync(path.join(outDir, `diagrams/${diagName}`), flowDiag);
let flowProse = '';
if (proseMod) {
console.log(`Generating prose for flow: ${fr.entryPoint}...`);
flowProse = await proseMod.describeFlow(fr, {});
flowProse = `${flowProse.trim()}\n\n`;
}
flowsContent += `## Flow: ${fr.entryPoint}\n`;
flowsContent += flowProse;
flowsContent += `**Subsystem Sequence:** ${fr.subsystemSequence.join(' → ')}\n\n`;
flowsContent += `\`\`\`mermaid\n${flowDiag}\n\`\`\`\n\n`;
}
@@ -128,19 +178,28 @@ 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);
const useProse = process.argv.includes('--prose');
const entryPoints = process.argv.slice(5).filter(a => a !== '--prose');
if (!snapshotPath || !srcRoot || !outDir) {
console.error('Usage: node sysdoc.js <snapshot.json> <srcRoot> <outDir> [entryPoint1] [entryPoint2] ...');
console.error('Usage: node sysdoc.js <snapshot.json> <srcRoot> <outDir> [--prose] [entryPoint1] ...');
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`);
// Using an IIFE to support top-level await
(async () => {
try {
const result = await generateDocs(graph, srcRoot, outDir, { entryPoints, prose: useProse });
console.log(`Generated docs in ${result.outDir}`);
console.log(`- ${result.subsystems} subsystems`);
console.log(`- ${result.contracts} contracts`);
console.log(`- ${result.flows} flows`);
} catch (err) {
console.error('Error generating docs:', err);
process.exit(1);
}
})();
}
module.exports = { generateDocs };