@cubis/foundry 0.3.78 → 0.3.79

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 (81) hide show
  1. package/dist/cli/build/commands.js +1 -1
  2. package/dist/cli/build/commands.js.map +1 -1
  3. package/dist/cli/core.js +601 -54
  4. package/dist/cli/core.js.map +1 -1
  5. package/package.json +1 -1
  6. package/src/cli/build/commands.ts +1 -1
  7. package/src/cli/core.ts +736 -54
  8. package/workflows/workflows/agent-environment-setup/generated/route-manifest.json +2 -2
  9. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/accessibility.toml +1 -1
  10. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/architecture.toml +2 -2
  11. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/backend.toml +1 -1
  12. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/create.toml +1 -1
  13. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/database.toml +1 -1
  14. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/debug.toml +1 -1
  15. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/devops.toml +1 -1
  16. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/implement-track.toml +1 -1
  17. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/migrate.toml +1 -1
  18. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/mobile.toml +1 -1
  19. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/onboard.toml +1 -1
  20. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/orchestrate.toml +1 -1
  21. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/plan.toml +1 -1
  22. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/refactor.toml +1 -1
  23. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/release.toml +1 -1
  24. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/review.toml +1 -1
  25. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/security.toml +1 -1
  26. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/spec.toml +1 -1
  27. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/test.toml +1 -1
  28. package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/vercel.toml +1 -1
  29. package/workflows/workflows/agent-environment-setup/platforms/antigravity/workflows/architecture.md +17 -12
  30. package/workflows/workflows/agent-environment-setup/platforms/antigravity/workflows/spec.md +2 -2
  31. package/workflows/workflows/agent-environment-setup/platforms/claude/workflows/architecture.md +17 -12
  32. package/workflows/workflows/agent-environment-setup/platforms/claude/workflows/spec.md +2 -2
  33. package/workflows/workflows/agent-environment-setup/platforms/codex/workflows/architecture.md +17 -12
  34. package/workflows/workflows/agent-environment-setup/platforms/codex/workflows/spec.md +2 -2
  35. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-accessibility.prompt.md +1 -1
  36. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-architecture.prompt.md +2 -2
  37. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-backend.prompt.md +1 -1
  38. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-create.prompt.md +1 -1
  39. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-database.prompt.md +1 -1
  40. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-debug.prompt.md +1 -1
  41. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-devops.prompt.md +1 -1
  42. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-implement-track.prompt.md +1 -1
  43. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-migrate.prompt.md +1 -1
  44. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-mobile.prompt.md +1 -1
  45. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-onboard.prompt.md +1 -1
  46. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-orchestrate.prompt.md +1 -1
  47. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-plan.prompt.md +1 -1
  48. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-refactor.prompt.md +1 -1
  49. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-release.prompt.md +1 -1
  50. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-review.prompt.md +1 -1
  51. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-security.prompt.md +1 -1
  52. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-spec.prompt.md +1 -1
  53. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-test.prompt.md +1 -1
  54. package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-vercel.prompt.md +1 -1
  55. package/workflows/workflows/agent-environment-setup/platforms/copilot/workflows/architecture.md +17 -12
  56. package/workflows/workflows/agent-environment-setup/platforms/copilot/workflows/spec.md +2 -2
  57. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/accessibility.toml +1 -1
  58. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/architecture.toml +2 -2
  59. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/backend.toml +1 -1
  60. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/create.toml +1 -1
  61. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/database.toml +1 -1
  62. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/debug.toml +1 -1
  63. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/devops.toml +1 -1
  64. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/implement-track.toml +1 -1
  65. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/migrate.toml +1 -1
  66. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/mobile.toml +1 -1
  67. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/onboard.toml +1 -1
  68. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/orchestrate.toml +1 -1
  69. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/plan.toml +1 -1
  70. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/refactor.toml +1 -1
  71. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/release.toml +1 -1
  72. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/review.toml +1 -1
  73. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/security.toml +1 -1
  74. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/spec.toml +1 -1
  75. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/test.toml +1 -1
  76. package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/vercel.toml +1 -1
  77. package/workflows/workflows/agent-environment-setup/platforms/gemini/workflows/architecture.md +17 -12
  78. package/workflows/workflows/agent-environment-setup/platforms/gemini/workflows/spec.md +2 -2
  79. package/workflows/workflows/agent-environment-setup/shared/rules/STEERING.md +3 -3
  80. package/workflows/workflows/agent-environment-setup/shared/workflows/architecture.md +17 -12
  81. package/workflows/workflows/agent-environment-setup/shared/workflows/spec.md +2 -2
package/src/cli/core.ts CHANGED
@@ -65,10 +65,22 @@ const ENGINEERING_ARCHITECTURE_BLOCK_START_RE =
65
65
  /<!--\s*cbx:architecture:rules:start[^>]*-->/g;
66
66
  const ENGINEERING_ARCHITECTURE_BLOCK_END_RE =
67
67
  /<!--\s*cbx:architecture:rules:end\s*-->/g;
68
+ const PRODUCT_FOUNDATION_BLOCK_START_RE =
69
+ /<!--\s*cbx:product:foundation:start[^>]*-->/g;
70
+ const PRODUCT_FOUNDATION_BLOCK_END_RE =
71
+ /<!--\s*cbx:product:foundation:end\s*-->/g;
72
+ const ARCHITECTURE_DOC_BLOCK_START_RE =
73
+ /<!--\s*cbx:architecture:doc:start[^>]*-->/g;
74
+ const ARCHITECTURE_DOC_BLOCK_END_RE =
75
+ /<!--\s*cbx:architecture:doc:end\s*-->/g;
68
76
  const TECH_ARCHITECTURE_BLOCK_START_RE =
69
77
  /<!--\s*cbx:architecture:tech:start[^>]*-->/g;
70
78
  const TECH_ARCHITECTURE_BLOCK_END_RE =
71
79
  /<!--\s*cbx:architecture:tech:end\s*-->/g;
80
+ const ROADMAP_FOUNDATION_BLOCK_START_RE =
81
+ /<!--\s*cbx:roadmap:foundation:start[^>]*-->/g;
82
+ const ROADMAP_FOUNDATION_BLOCK_END_RE =
83
+ /<!--\s*cbx:roadmap:foundation:end\s*-->/g;
72
84
  const COPILOT_ALLOWED_SKILL_FRONTMATTER_KEYS = new Set([
73
85
  "compatibility",
74
86
  "description",
@@ -1105,6 +1117,181 @@ function buildArchitectureMermaid(snapshot) {
1105
1117
  return lines.join("\n");
1106
1118
  }
1107
1119
 
1120
+ function inferProductFoundationProfile(snapshot, specRoots = []) {
1121
+ const appRoots = (snapshot.architectureByApp || [])
1122
+ .map((item) => item.rootPath)
1123
+ .filter((value) => value && value !== ".");
1124
+ const primarySurfaces = appRoots.length > 0 ? appRoots : snapshot.topDirs.slice(0, 6);
1125
+ const userPersonas = [];
1126
+
1127
+ if (snapshot.frameworks.includes("Flutter")) {
1128
+ userPersonas.push("End users interacting through mobile or desktop app surfaces");
1129
+ }
1130
+ if (
1131
+ snapshot.frameworks.includes("Next.js") ||
1132
+ snapshot.frameworks.includes("React") ||
1133
+ snapshot.topDirs.includes("web")
1134
+ ) {
1135
+ userPersonas.push("Browser users and internal operators using web-facing flows");
1136
+ }
1137
+ if (snapshot.frameworks.includes("NestJS") || snapshot.topDirs.includes("api")) {
1138
+ userPersonas.push("Internal services, operators, or partner systems consuming API boundaries");
1139
+ }
1140
+ if (userPersonas.length === 0) {
1141
+ userPersonas.push(
1142
+ "Project stakeholders are not obvious from the repo alone; refine personas from product context before major feature work.",
1143
+ );
1144
+ }
1145
+
1146
+ const coreJourneys = [];
1147
+ if (specRoots.length > 0) {
1148
+ coreJourneys.push(
1149
+ `Active implementation themes are reflected in current spec packs: ${specRoots.join(", ")}.`,
1150
+ );
1151
+ }
1152
+ if (primarySurfaces.length > 0) {
1153
+ coreJourneys.push(
1154
+ `Primary product surfaces currently live in: ${primarySurfaces.join(", ")}.`,
1155
+ );
1156
+ }
1157
+ if (snapshot.isMcpServer || snapshot.mcpSignals.length > 0) {
1158
+ coreJourneys.push("Tool-assisted and MCP-driven workflows are part of the operating model and should stay stable.");
1159
+ }
1160
+ if (coreJourneys.length === 0) {
1161
+ coreJourneys.push(
1162
+ "Repository evidence is thin; capture the primary user journeys here before scaling the codebase further.",
1163
+ );
1164
+ }
1165
+
1166
+ const successSignals = [
1167
+ "Feature work should stay aligned to explicit user or operator value, not speculative abstractions.",
1168
+ "Architecture changes should reduce onboarding, debugging, and testing cost over time.",
1169
+ ];
1170
+ if (snapshot.cicdSignals.length > 0) {
1171
+ successSignals.push(
1172
+ `Delivery flows already rely on ${snapshot.cicdSignals.join(", ")} signals; keep release friction low for that pipeline.`,
1173
+ );
1174
+ }
1175
+
1176
+ return {
1177
+ productScope:
1178
+ snapshot.readmeExcerpt ||
1179
+ "No explicit product summary was detected from repo docs. Replace this with a concise product statement when better context exists.",
1180
+ primarySurfaces,
1181
+ userPersonas,
1182
+ coreJourneys,
1183
+ successSignals,
1184
+ };
1185
+ }
1186
+
1187
+ function buildProductFoundationSection(snapshot, specRoots = []) {
1188
+ const profile = inferProductFoundationProfile(snapshot, specRoots);
1189
+ const hash = hashStableObject(profile);
1190
+
1191
+ return [
1192
+ `<!-- cbx:product:foundation:start version=1 profile=${hash} -->`,
1193
+ "## Product Foundation (auto-managed)",
1194
+ "",
1195
+ "### Product Scope",
1196
+ profile.productScope,
1197
+ "",
1198
+ "### Primary Surfaces",
1199
+ ...(profile.primarySurfaces.length > 0
1200
+ ? profile.primarySurfaces.map((item) => `- ${item}`)
1201
+ : ["- No primary surfaces were detected automatically."]),
1202
+ "",
1203
+ "### Users and Operators",
1204
+ ...profile.userPersonas.map((item) => `- ${item}`),
1205
+ "",
1206
+ "### Core Journeys",
1207
+ ...profile.coreJourneys.map((item) => `- ${item}`),
1208
+ "",
1209
+ "### Success Signals",
1210
+ ...profile.successSignals.map((item) => `- ${item}`),
1211
+ "",
1212
+ "### Product Guardrails",
1213
+ "- Keep product intent stable across future features so agents do not optimize for the wrong user outcome.",
1214
+ "- Refresh this managed section when scope, personas, operating model, or success metrics change materially.",
1215
+ "<!-- cbx:product:foundation:end -->",
1216
+ "",
1217
+ ].join("\n");
1218
+ }
1219
+
1220
+ function inferArchitectureDocProfile(snapshot, specRoots = []) {
1221
+ const contract = inferArchitectureContractProfile(snapshot);
1222
+ const architectureSignals = (snapshot.architectureByApp || []).map((item) => {
1223
+ const label = item.rootPath === "." ? "repo root" : item.rootPath;
1224
+ const signals =
1225
+ item.architectureSignals && item.architectureSignals.length > 0
1226
+ ? item.architectureSignals.join(", ")
1227
+ : "not enough signals to classify";
1228
+ return `${label}: ${signals}`;
1229
+ });
1230
+ const decisionAreas = [
1231
+ "Module boundaries and dependency direction",
1232
+ "Design-system ownership and reusable primitives",
1233
+ "Testing and validation expectations by runtime boundary",
1234
+ ];
1235
+ if (specRoots.length > 0) {
1236
+ decisionAreas.push(
1237
+ `Active specs that may influence upcoming architecture work: ${specRoots.join(", ")}.`,
1238
+ );
1239
+ }
1240
+
1241
+ return {
1242
+ style: contract.style,
1243
+ designSystemSource: contract.designSystemSource,
1244
+ moduleBoundaries: contract.moduleBoundaries,
1245
+ architectureSignals,
1246
+ decisionAreas,
1247
+ scalingConstraints: contract.scalingConstraints,
1248
+ testingStrategy: contract.testingStrategy,
1249
+ };
1250
+ }
1251
+
1252
+ function buildArchitectureDocSection(snapshot, specRoots = []) {
1253
+ const profile = inferArchitectureDocProfile(snapshot, specRoots);
1254
+ const hash = hashStableObject(profile);
1255
+
1256
+ return [
1257
+ `<!-- cbx:architecture:doc:start version=1 profile=${hash} -->`,
1258
+ "## Accepted Architecture Backbone (auto-managed)",
1259
+ "",
1260
+ "### System Overview",
1261
+ `- Accepted style: ${profile.style}.`,
1262
+ `- Design-system source of truth: ${profile.designSystemSource}`,
1263
+ "",
1264
+ "### Bounded Contexts and Module Boundaries",
1265
+ ...(profile.moduleBoundaries.length > 0
1266
+ ? profile.moduleBoundaries.map((item) => `- ${item}`)
1267
+ : ["- No strong top-level module boundaries were detected automatically."]),
1268
+ "",
1269
+ "### Architecture Signals by Surface",
1270
+ ...(profile.architectureSignals.length > 0
1271
+ ? profile.architectureSignals.map((item) => `- ${item}`)
1272
+ : ["- No app-level architecture signals were inferred from the repo scan."]),
1273
+ "",
1274
+ "### Decision Areas to Preserve",
1275
+ ...profile.decisionAreas.map((item) => `- ${item}`),
1276
+ "",
1277
+ "### Scalability and Reliability Notes",
1278
+ ...profile.scalingConstraints.map((item) => `- ${item}`),
1279
+ "",
1280
+ "### Testing and Operability",
1281
+ ...profile.testingStrategy.map((item) => `- ${item}`),
1282
+ "",
1283
+ "### ADR Linkage",
1284
+ "- Keep durable architecture decisions in `docs/adr/` and summarize the active decision set here.",
1285
+ "",
1286
+ "### Mermaid Diagram",
1287
+ "```mermaid",
1288
+ buildArchitectureDocMermaid(snapshot),
1289
+ "```",
1290
+ "<!-- cbx:architecture:doc:end -->",
1291
+ "",
1292
+ ].join("\n");
1293
+ }
1294
+
1108
1295
  function buildEngineeringArchitectureSection(snapshot) {
1109
1296
  const profile = inferArchitectureContractProfile(snapshot);
1110
1297
  const hash = hashStableObject(profile);
@@ -1125,12 +1312,32 @@ function buildEngineeringArchitectureSection(snapshot) {
1125
1312
  ...profile.testingStrategy.map((rule) => ` - ${rule}`),
1126
1313
  "- Doc refresh policy:",
1127
1314
  " - Update these managed sections when architecture, scale, boundaries, design-system rules, or testing strategy changes.",
1128
- " - For non-trivial work, read this file before planning and read TECH.md next.",
1315
+ " - For non-trivial work, read PRODUCT.md, ENGINEERING_RULES.md, ARCHITECTURE.md, and TECH.md in that order when they exist.",
1129
1316
  "<!-- cbx:architecture:rules:end -->",
1130
1317
  "",
1131
1318
  ].join("\n");
1132
1319
  }
1133
1320
 
1321
+ function buildArchitectureDocMermaid(snapshot) {
1322
+ const topDirs = snapshot.topDirs.slice(0, 5);
1323
+ const lines = [
1324
+ "flowchart LR",
1325
+ ' product["Product Intent"] --> rules["Engineering Rules"]',
1326
+ ' product --> arch["Architecture Backbone"]',
1327
+ ' arch --> tech["Current Tech Snapshot"]',
1328
+ ];
1329
+ if (topDirs.length > 0) {
1330
+ for (let index = 0; index < topDirs.length; index += 1) {
1331
+ const dir = topDirs[index];
1332
+ const nodeName = `D${index}`;
1333
+ lines.push(` arch --> ${nodeName}["${dir}/"]`);
1334
+ }
1335
+ }
1336
+ lines.push(' arch --> adr["docs/adr"]');
1337
+ lines.push(' product --> specs["docs/specs"]');
1338
+ return lines.join("\n");
1339
+ }
1340
+
1134
1341
  function buildTechArchitectureSection(snapshot) {
1135
1342
  const profile = inferArchitectureContractProfile(snapshot);
1136
1343
  const payload = {
@@ -1186,6 +1393,56 @@ function buildTechArchitectureSection(snapshot) {
1186
1393
  ].join("\n");
1187
1394
  }
1188
1395
 
1396
+ function buildRoadmapFoundationSection(snapshot, specRoots = []) {
1397
+ const payload = {
1398
+ topDirs: snapshot.topDirs,
1399
+ frameworks: snapshot.frameworks,
1400
+ specRoots,
1401
+ };
1402
+ const hash = hashStableObject(payload);
1403
+ const nowItems = specRoots.length > 0
1404
+ ? specRoots.map((item) => `Track active change planning in \`${item}\`.`)
1405
+ : [
1406
+ "No active spec packs detected. Create a spec pack before starting the next non-trivial feature or migration.",
1407
+ ];
1408
+ const nextItems = [];
1409
+ if (snapshot.frameworks.length > 0) {
1410
+ nextItems.push(
1411
+ `Keep shared conventions stable across the current stack: ${snapshot.frameworks.join(", ")}.`,
1412
+ );
1413
+ }
1414
+ if (snapshot.cicdSignals.length > 0) {
1415
+ nextItems.push(
1416
+ `Preserve release compatibility with the detected delivery surfaces: ${snapshot.cicdSignals.join(", ")}.`,
1417
+ );
1418
+ }
1419
+ if (nextItems.length === 0) {
1420
+ nextItems.push(
1421
+ "Document the next scaling milestones here once product direction and architecture constraints are clearer.",
1422
+ );
1423
+ }
1424
+
1425
+ return [
1426
+ `<!-- cbx:roadmap:foundation:start version=1 profile=${hash} -->`,
1427
+ "## Delivery Backbone (auto-managed)",
1428
+ "",
1429
+ "### Now",
1430
+ ...nowItems.map((item) => `- ${item}`),
1431
+ "",
1432
+ "### Next",
1433
+ ...nextItems.map((item) => `- ${item}`),
1434
+ "",
1435
+ "### Later",
1436
+ "- Use this section for medium-term scaling themes, major migrations, or cross-team architecture investments.",
1437
+ "",
1438
+ "### Backbone Maintenance",
1439
+ "- Keep PRODUCT.md, ARCHITECTURE.md, ENGINEERING_RULES.md, and TECH.md aligned when direction or structure changes.",
1440
+ "- Link major roadmap themes back to specs and ADRs instead of burying them in chat-only planning.",
1441
+ "<!-- cbx:roadmap:foundation:end -->",
1442
+ "",
1443
+ ].join("\n");
1444
+ }
1445
+
1189
1446
  function buildEngineeringRulesTemplate() {
1190
1447
  return [
1191
1448
  "# Engineering Rules",
@@ -1288,14 +1545,105 @@ function buildEngineeringRulesTemplate() {
1288
1545
  ].join("\n");
1289
1546
  }
1290
1547
 
1548
+ function buildProductTemplate(snapshot, specRoots = []) {
1549
+ return [
1550
+ "# Product",
1551
+ "",
1552
+ "This file is the durable product backbone for the project.",
1553
+ "",
1554
+ buildProductFoundationSection(snapshot, specRoots).trimEnd(),
1555
+ "",
1556
+ ].join("\n");
1557
+ }
1558
+
1559
+ function buildArchitectureDocTemplate(snapshot, specRoots = []) {
1560
+ return [
1561
+ "# Architecture",
1562
+ "",
1563
+ "This file captures the accepted architecture backbone for the project.",
1564
+ "",
1565
+ buildArchitectureDocSection(snapshot, specRoots).trimEnd(),
1566
+ "",
1567
+ ].join("\n");
1568
+ }
1569
+
1570
+ function buildRoadmapTemplate(snapshot, specRoots = []) {
1571
+ return [
1572
+ "# Roadmap",
1573
+ "",
1574
+ "This file captures the living delivery backbone for medium-term product and architecture work.",
1575
+ "",
1576
+ buildRoadmapFoundationSection(snapshot, specRoots).trimEnd(),
1577
+ "",
1578
+ ].join("\n");
1579
+ }
1580
+
1581
+ function buildAdrReadme() {
1582
+ return [
1583
+ "# Architecture Decision Records",
1584
+ "",
1585
+ "Use this directory for durable decisions that future contributors and agents need to preserve.",
1586
+ "",
1587
+ "## When to add an ADR",
1588
+ "",
1589
+ "- Architecture style or boundary changes",
1590
+ "- Data model or persistence strategy changes",
1591
+ "- Deployment or scaling model changes",
1592
+ "- Design-system ownership or shared UX pattern changes",
1593
+ "",
1594
+ "## Suggested format",
1595
+ "",
1596
+ "1. Context",
1597
+ "2. Decision",
1598
+ "3. Consequences",
1599
+ "4. Validation",
1600
+ "",
1601
+ "Start with `0000-template.md` and create numbered follow-up ADRs for accepted decisions.",
1602
+ "",
1603
+ ].join("\n");
1604
+ }
1605
+
1606
+ function buildAdrTemplate() {
1607
+ return [
1608
+ "# ADR 0000: Title",
1609
+ "",
1610
+ "## Status",
1611
+ "",
1612
+ "Proposed",
1613
+ "",
1614
+ "## Context",
1615
+ "",
1616
+ "- What problem or pressure led to this decision?",
1617
+ "",
1618
+ "## Decision",
1619
+ "",
1620
+ "- What is the chosen direction?",
1621
+ "",
1622
+ "## Consequences",
1623
+ "",
1624
+ "- What tradeoffs, benefits, or costs follow from this choice?",
1625
+ "",
1626
+ "## Validation",
1627
+ "",
1628
+ "- How will the team know this decision is working?",
1629
+ "",
1630
+ ].join("\n");
1631
+ }
1632
+
1291
1633
  function buildEngineeringRulesManagedBlock({
1292
1634
  platform,
1635
+ productFilePath,
1636
+ architectureFilePath,
1293
1637
  engineeringRulesFilePath,
1294
1638
  techMdFilePath,
1639
+ roadmapFilePath,
1295
1640
  ruleFilePath,
1296
1641
  }) {
1642
+ const productRef = toPosixPath(path.resolve(productFilePath));
1643
+ const architectureRef = toPosixPath(path.resolve(architectureFilePath));
1297
1644
  const engineeringRef = toPosixPath(path.resolve(engineeringRulesFilePath));
1298
1645
  const techRef = toPosixPath(path.resolve(techMdFilePath));
1646
+ const roadmapRef = toPosixPath(path.resolve(roadmapFilePath));
1299
1647
  const ruleRef = toPosixPath(path.resolve(ruleFilePath));
1300
1648
 
1301
1649
  return [
@@ -1303,8 +1651,11 @@ function buildEngineeringRulesManagedBlock({
1303
1651
  "## Engineering Guardrails (auto-managed)",
1304
1652
  "Apply these before planning, coding, review, and release:",
1305
1653
  "",
1654
+ `- Product backbone: \`${productRef}\``,
1655
+ `- Accepted architecture: \`${architectureRef}\``,
1306
1656
  `- Required baseline: \`${engineeringRef}\``,
1307
1657
  `- Project tech map: \`${techRef}\``,
1658
+ `- Delivery roadmap: \`${roadmapRef}\``,
1308
1659
  `- Active platform rule file: \`${ruleRef}\``,
1309
1660
  "",
1310
1661
  "Hard policy:",
@@ -1312,7 +1663,7 @@ function buildEngineeringRulesManagedBlock({
1312
1663
  "2. Keep architecture simple (KISS) and avoid speculative work (YAGNI).",
1313
1664
  "3. Apply SOLID pragmatically to reduce change risk, not add ceremony.",
1314
1665
  "4. Use clear naming with focused responsibilities and explicit boundaries.",
1315
- "5. For non-trivial work, read ENGINEERING_RULES.md first and TECH.md next before planning or implementation.",
1666
+ "5. For non-trivial work, read PRODUCT.md, ENGINEERING_RULES.md, ARCHITECTURE.md, and TECH.md in that order when they exist before planning or implementation.",
1316
1667
  "6. Require validation evidence (lint/types/tests) before merge.",
1317
1668
  "7. Use Decision Log response style.",
1318
1669
  "8. Every Decision Log must include a `Skills Used` section listing skill, workflow, or agent names.",
@@ -1437,14 +1788,20 @@ async function upsertEngineeringRulesFile({
1437
1788
  async function upsertEngineeringRulesBlock({
1438
1789
  ruleFilePath,
1439
1790
  platform,
1791
+ productFilePath,
1792
+ architectureFilePath,
1440
1793
  engineeringRulesFilePath,
1441
1794
  techMdFilePath,
1795
+ roadmapFilePath,
1442
1796
  dryRun = false,
1443
1797
  }) {
1444
1798
  const block = buildEngineeringRulesManagedBlock({
1445
1799
  platform,
1800
+ productFilePath,
1801
+ architectureFilePath,
1446
1802
  engineeringRulesFilePath,
1447
1803
  techMdFilePath,
1804
+ roadmapFilePath,
1448
1805
  ruleFilePath,
1449
1806
  });
1450
1807
  const exists = await pathExists(ruleFilePath);
@@ -1558,28 +1915,59 @@ async function upsertTaggedSectionInFile({
1558
1915
  function buildArchitectureBuildMetadata({
1559
1916
  platform,
1560
1917
  researchMode,
1918
+ productProfileHash,
1919
+ architectureDocHash,
1561
1920
  rulesProfileHash,
1562
1921
  techSnapshotHash,
1922
+ roadmapProfileHash,
1563
1923
  }) {
1564
1924
  return {
1565
- schemaVersion: 1,
1925
+ schemaVersion: 2,
1566
1926
  generatedBy: "cbx build architecture",
1567
1927
  generatedAt: new Date().toISOString(),
1568
1928
  platform,
1569
1929
  researchMode,
1930
+ productProfileHash,
1931
+ architectureDocHash,
1570
1932
  rulesProfileHash,
1571
1933
  techSnapshotHash,
1934
+ roadmapProfileHash,
1572
1935
  };
1573
1936
  }
1574
1937
 
1575
1938
  async function ensureArchitectureDocScaffold({
1576
1939
  workspaceRoot,
1577
1940
  snapshot,
1941
+ specRoots = [],
1578
1942
  overwrite = false,
1579
1943
  dryRun = false,
1580
1944
  }) {
1945
+ const productPath = path.join(workspaceRoot, "PRODUCT.md");
1946
+ const architectureDocPath = path.join(workspaceRoot, "ARCHITECTURE.md");
1581
1947
  const engineeringRulesPath = path.join(workspaceRoot, "ENGINEERING_RULES.md");
1582
1948
  const techMdPath = path.join(workspaceRoot, "TECH.md");
1949
+ const roadmapPath = path.join(workspaceRoot, "ROADMAP.md");
1950
+ const adrDir = path.join(workspaceRoot, "docs", "adr");
1951
+ const adrReadmePath = path.join(adrDir, "README.md");
1952
+ const adrTemplatePath = path.join(adrDir, "0000-template.md");
1953
+
1954
+ const productResult = await upsertTaggedSectionInFile({
1955
+ targetPath: productPath,
1956
+ initialContent: `${buildProductTemplate(snapshot, specRoots)}\n`,
1957
+ block: buildProductFoundationSection(snapshot, specRoots),
1958
+ startPattern: PRODUCT_FOUNDATION_BLOCK_START_RE,
1959
+ endPattern: PRODUCT_FOUNDATION_BLOCK_END_RE,
1960
+ dryRun,
1961
+ });
1962
+
1963
+ const architectureDocResult = await upsertTaggedSectionInFile({
1964
+ targetPath: architectureDocPath,
1965
+ initialContent: `${buildArchitectureDocTemplate(snapshot, specRoots)}\n`,
1966
+ block: buildArchitectureDocSection(snapshot, specRoots),
1967
+ startPattern: ARCHITECTURE_DOC_BLOCK_START_RE,
1968
+ endPattern: ARCHITECTURE_DOC_BLOCK_END_RE,
1969
+ dryRun,
1970
+ });
1583
1971
 
1584
1972
  const rulesTemplate = buildEngineeringRulesTemplate();
1585
1973
  const rulesFileResult = await upsertEngineeringRulesFile({
@@ -1613,13 +2001,45 @@ async function ensureArchitectureDocScaffold({
1613
2001
  dryRun,
1614
2002
  });
1615
2003
 
2004
+ const roadmapResult = await upsertTaggedSectionInFile({
2005
+ targetPath: roadmapPath,
2006
+ initialContent: `${buildRoadmapTemplate(snapshot, specRoots)}\n`,
2007
+ block: buildRoadmapFoundationSection(snapshot, specRoots),
2008
+ startPattern: ROADMAP_FOUNDATION_BLOCK_START_RE,
2009
+ endPattern: ROADMAP_FOUNDATION_BLOCK_END_RE,
2010
+ dryRun,
2011
+ });
2012
+
2013
+ const adrReadmeResult = await writeTextFile({
2014
+ targetPath: adrReadmePath,
2015
+ content: `${buildAdrReadme()}\n`,
2016
+ overwrite,
2017
+ dryRun,
2018
+ });
2019
+ const adrTemplateResult = await writeTextFile({
2020
+ targetPath: adrTemplatePath,
2021
+ content: `${buildAdrTemplate()}\n`,
2022
+ overwrite,
2023
+ dryRun,
2024
+ });
2025
+
1616
2026
  return {
2027
+ productPath,
2028
+ architectureDocPath,
1617
2029
  engineeringRulesPath,
1618
2030
  techMdPath,
2031
+ roadmapPath,
2032
+ adrReadmePath,
2033
+ adrTemplatePath,
2034
+ productResult,
2035
+ architectureDocResult,
1619
2036
  rulesFileResult,
1620
2037
  rulesArchitectureResult,
1621
2038
  techResult,
1622
2039
  techArchitectureResult,
2040
+ roadmapResult,
2041
+ adrReadmeResult,
2042
+ adrTemplateResult,
1623
2043
  };
1624
2044
  }
1625
2045
 
@@ -8755,14 +9175,6 @@ async function performWorkflowInstall(
8755
9175
  dryRun,
8756
9176
  cwd,
8757
9177
  });
8758
- const engineeringArtifactsResult = await upsertEngineeringArtifacts({
8759
- platform,
8760
- scope: ruleScope,
8761
- overwrite: false,
8762
- dryRun,
8763
- skipTech: false,
8764
- cwd,
8765
- });
8766
9178
  const postmanSetupResult = await configurePostmanInstallArtifacts({
8767
9179
  platform,
8768
9180
  scope,
@@ -8808,7 +9220,7 @@ async function performWorkflowInstall(
8808
9220
  bundleId,
8809
9221
  installResult,
8810
9222
  syncResult,
8811
- engineeringArtifactsResult,
9223
+ engineeringArtifactsResult: null,
8812
9224
  postmanSetupResult,
8813
9225
  terminalVerificationRuleResult,
8814
9226
  };
@@ -8838,10 +9250,7 @@ async function runWorkflowInstall(options) {
8838
9250
  dryRun: result.dryRun,
8839
9251
  });
8840
9252
  printRuleSyncResult(result.syncResult);
8841
- printInstallEngineeringSummary({
8842
- engineeringResults: result.engineeringArtifactsResult.engineeringResults,
8843
- techResult: result.engineeringArtifactsResult.techResult,
8844
- });
9253
+ printInstallDocumentationNotice();
8845
9254
  printPostmanSetupSummary({
8846
9255
  postmanSetup: result.postmanSetupResult,
8847
9256
  });
@@ -12296,6 +12705,16 @@ function printInstallEngineeringSummary({ engineeringResults, techResult }) {
12296
12705
  }
12297
12706
  }
12298
12707
 
12708
+ function printInstallDocumentationNotice() {
12709
+ console.log("\nProject backbone docs:");
12710
+ console.log(
12711
+ "- Install only wires the rule references and workflow assets.",
12712
+ );
12713
+ console.log(
12714
+ "- Use `cbx rules init` to scaffold ENGINEERING_RULES.md and TECH.md, or `cbx build architecture --platform <codex|claude|gemini|copilot>` to generate PRODUCT.md, ARCHITECTURE.md, ENGINEERING_RULES.md, TECH.md, ROADMAP.md, and ADR scaffolds.",
12715
+ );
12716
+ }
12717
+
12299
12718
  async function upsertEngineeringArtifacts({
12300
12719
  platform,
12301
12720
  scope,
@@ -12313,10 +12732,10 @@ async function upsertEngineeringArtifacts({
12313
12732
  const scaffold = await ensureArchitectureDocScaffold({
12314
12733
  workspaceRoot,
12315
12734
  snapshot,
12735
+ specRoots: [],
12316
12736
  overwrite,
12317
12737
  dryRun,
12318
12738
  });
12319
- const techMdPath = path.join(workspaceRoot, "TECH.md");
12320
12739
  const targets = [{ ruleFilePath }];
12321
12740
 
12322
12741
  if (scope === "global") {
@@ -12341,8 +12760,11 @@ async function upsertEngineeringArtifacts({
12341
12760
  const blockResult = await upsertEngineeringRulesBlock({
12342
12761
  ruleFilePath: target.ruleFilePath,
12343
12762
  platform,
12763
+ productFilePath: scaffold.productPath,
12764
+ architectureFilePath: scaffold.architectureDocPath,
12344
12765
  engineeringRulesFilePath: scaffold.engineeringRulesPath,
12345
- techMdFilePath: techMdPath,
12766
+ techMdFilePath: scaffold.techMdPath,
12767
+ roadmapFilePath: scaffold.roadmapPath,
12346
12768
  dryRun,
12347
12769
  });
12348
12770
  engineeringResults.push({
@@ -12541,8 +12963,12 @@ function buildArchitecturePrompt({
12541
12963
  conditionalSkills,
12542
12964
  skillPathHints,
12543
12965
  }) {
12966
+ const productPath = "PRODUCT.md";
12967
+ const architecturePath = "ARCHITECTURE.md";
12544
12968
  const rulesPath = "ENGINEERING_RULES.md";
12545
12969
  const techPath = "TECH.md";
12970
+ const roadmapPath = "ROADMAP.md";
12971
+ const adrReadmePath = "docs/adr/README.md";
12546
12972
  const architectureSignals = snapshot.architectureByApp
12547
12973
  .filter((item) => (item.architectureSignals || []).length > 0)
12548
12974
  .map((item) => {
@@ -12554,9 +12980,9 @@ function buildArchitecturePrompt({
12554
12980
  `You are running inside ${platform}.`,
12555
12981
  "",
12556
12982
  "Objective:",
12557
- `- Inspect the repository at ${toPosixPath(workspaceRoot)} and refresh the managed architecture sections in ${rulesPath} and ${techPath}.`,
12558
- "- Keep ENGINEERING_RULES.md normative and TECH.md descriptive.",
12559
- "- Preserve manual content outside the managed `cbx:architecture:*` markers.",
12983
+ `- Inspect the repository at ${toPosixPath(workspaceRoot)} and refresh the scalable project backbone in ${productPath}, ${architecturePath}, ${rulesPath}, ${techPath}, and ${roadmapPath}.`,
12984
+ "- Keep PRODUCT.md focused on intent, ARCHITECTURE.md on accepted structure, ENGINEERING_RULES.md on normative rules, TECH.md on current-state evidence, and ROADMAP.md on delivery themes.",
12985
+ "- Preserve manual content outside the managed `cbx:*` markers.",
12560
12986
  "",
12561
12987
  "Required skill bundle:",
12562
12988
  `- Load these exact skill IDs first: ${coreSkills.map((skillId) => `\`${skillId}\``).join(", ")}`,
@@ -12577,22 +13003,25 @@ function buildArchitecturePrompt({
12577
13003
  : "- Architecture signals: none confidently inferred from the repo scan",
12578
13004
  "",
12579
13005
  "Execution contract:",
12580
- "1. Read ENGINEERING_RULES.md first if present, then read TECH.md.",
13006
+ `1. Read ${productPath}, ${rulesPath}, ${architecturePath}, and ${techPath} in that order when they exist.`,
12581
13007
  "2. Inspect the repo before making architecture claims.",
12582
- "3. Update only the content between the existing `cbx:architecture:rules` and `cbx:architecture:tech` markers.",
12583
- "4. Keep the marker lines themselves intact, including their hash metadata.",
12584
- "5. In ENGINEERING_RULES.md, state architecture style, dependency rules, feature or module structure rules, design-system source of truth, testability expectations, and doc refresh policy.",
12585
- "6. In TECH.md, update architecture snapshot, module or app topology, flow narratives, Mermaid diagrams, and scaling or deployment notes.",
13008
+ "3. Update only the content between the existing managed markers in the backbone docs and preserve the marker lines themselves, including their hash metadata.",
13009
+ "4. In PRODUCT.md, state product scope, primary surfaces, users or operators, core journeys, success signals, and product guardrails.",
13010
+ "5. In ARCHITECTURE.md, state accepted architecture style, bounded contexts, stable decision areas, ADR linkage, and add at least one Mermaid architecture diagram if the repo is non-trivial.",
13011
+ "6. In ENGINEERING_RULES.md, state architecture style, dependency rules, feature or module structure rules, design-system source of truth, testability expectations, and doc refresh policy.",
13012
+ "7. In TECH.md, update architecture snapshot, module or app topology, flow narratives, Mermaid diagrams, and scaling or deployment notes.",
13013
+ "8. In ROADMAP.md, capture current delivery themes, active spec-driven work, and major architecture follow-ups without turning it into a speculative wishlist.",
12586
13014
  researchMode === "never"
12587
- ? "7. Stay repo-only. Do not use outside research."
12588
- : "7. Use repo evidence first. Use official docs when needed. Treat Reddit or community sources only as labeled secondary evidence.",
13015
+ ? "9. Stay repo-only. Do not use outside research."
13016
+ : "9. Use repo evidence first. Use official docs when needed. Treat Reddit or community sources only as labeled secondary evidence.",
12589
13017
  researchMode === "always"
12590
- ? "8. Include an external research evidence subsection in TECH.md with clearly labeled primary and secondary evidence."
12591
- : "8. Include external research notes only if they materially informed the architecture update.",
12592
- "9. If the project clearly follows Clean Architecture, feature-first modules, or another stable structure, make that explicit so future implementation stays consistent.",
13018
+ ? "10. Include an external research evidence subsection in TECH.md with clearly labeled primary and secondary evidence."
13019
+ : "10. Include external research notes only if they materially informed the architecture update.",
13020
+ "11. If the project clearly follows Clean Architecture, feature-first modules, or another stable structure, make that explicit so future implementation stays consistent.",
13021
+ `12. Ensure ${adrReadmePath} exists as the ADR entrypoint and mention ADR follow-up when the repo lacks decision history.`,
12593
13022
  "",
12594
13023
  "Return one JSON object on the last line with this shape:",
12595
- '{"files_written":["ENGINEERING_RULES.md","TECH.md"],"research_used":false,"gaps":[],"next_actions":[]}',
13024
+ '{"files_written":["PRODUCT.md","ARCHITECTURE.md","ENGINEERING_RULES.md","TECH.md","ROADMAP.md","docs/adr/README.md"],"research_used":false,"gaps":[],"next_actions":[]}',
12596
13025
  "",
12597
13026
  "Do not emit placeholder TODOs in the managed sections.",
12598
13027
  ].join("\n");
@@ -12622,6 +13051,95 @@ async function execFileCapture(command, args, options = {}) {
12622
13051
  }
12623
13052
  }
12624
13053
 
13054
+ async function spawnCapture(command, args, options = {}) {
13055
+ const { cwd, env, streamOutput = false } = options;
13056
+ return await new Promise((resolve, reject) => {
13057
+ let stdout = "";
13058
+ let stderr = "";
13059
+ const child = spawn(command, args, {
13060
+ cwd,
13061
+ env,
13062
+ stdio: ["ignore", "pipe", "pipe"],
13063
+ });
13064
+
13065
+ child.stdout.on("data", (chunk) => {
13066
+ const text = chunk.toString();
13067
+ stdout += text;
13068
+ if (streamOutput) process.stdout.write(text);
13069
+ });
13070
+
13071
+ child.stderr.on("data", (chunk) => {
13072
+ const text = chunk.toString();
13073
+ stderr += text;
13074
+ if (streamOutput) process.stderr.write(text);
13075
+ });
13076
+
13077
+ child.on("error", (error) => {
13078
+ if (error?.code === "ENOENT") {
13079
+ reject(
13080
+ new Error(`Required CLI '${command}' is not installed or not on PATH.`),
13081
+ );
13082
+ return;
13083
+ }
13084
+ reject(error);
13085
+ });
13086
+
13087
+ child.on("close", (code) => {
13088
+ resolve({
13089
+ ok: code === 0,
13090
+ stdout,
13091
+ stderr,
13092
+ code: code ?? 1,
13093
+ });
13094
+ });
13095
+ });
13096
+ }
13097
+
13098
+ function explainArchitectureBuildFailure(platform, execution) {
13099
+ const combined = String(
13100
+ `${execution.stderr || ""}\n${execution.stdout || ""}`.trim(),
13101
+ );
13102
+ const notes = [];
13103
+
13104
+ if (platform === "gemini") {
13105
+ if (
13106
+ combined.includes("Error during discovery for MCP server") ||
13107
+ combined.includes("[MCP error]")
13108
+ ) {
13109
+ notes.push(
13110
+ "Gemini CLI is failing while loading MCP servers from your Gemini settings. Start the required MCP runtime(s) first or disable the broken server entries in `.gemini/settings.json` before retrying.",
13111
+ );
13112
+ }
13113
+ if (
13114
+ combined.includes("cloudaicompanion.companions.generateChat") ||
13115
+ combined.includes("PERMISSION_DENIED") ||
13116
+ combined.includes("403")
13117
+ ) {
13118
+ notes.push(
13119
+ "Gemini CLI reached Google auth, but the active account or project cannot generate chat content. Re-authenticate Gemini CLI with a permitted account or configure a supported Gemini API credential and project before retrying.",
13120
+ );
13121
+ }
13122
+ }
13123
+
13124
+ if (platform === "claude" && combined.includes("permission")) {
13125
+ notes.push(
13126
+ "Claude CLI appears to be blocked by its own permission model. Re-run in a Claude environment that allows non-interactive edits for this workspace.",
13127
+ );
13128
+ }
13129
+
13130
+ if (notes.length === 0) {
13131
+ return `Architecture build failed via ${platform}. ${combined}`.trim();
13132
+ }
13133
+
13134
+ return [
13135
+ `Architecture build failed via ${platform}.`,
13136
+ ...notes.map((note) => `- ${note}`),
13137
+ combined ? `Raw CLI output:\n${combined}` : "",
13138
+ ]
13139
+ .filter(Boolean)
13140
+ .join("\n");
13141
+ }
13142
+
12625
13143
  async function probeArchitectureAdapter(platform, cwd) {
12626
13144
  if (platform === "codex") {
12627
13145
  const help = await execFileCapture("codex", ["exec", "--help"], { cwd });
@@ -12710,9 +13228,8 @@ async function probeArchitectureAdapter(platform, cwd) {
12710
13228
  function normalizeArchitectureResult({
12711
13229
  stdout,
12712
13230
  workspaceRoot,
12713
- rulesPath,
12714
- techPath,
12715
13231
  researchMode,
13232
+ changedFiles = [],
12716
13233
  }) {
12717
13234
  const trimmed = String(stdout || "").trim();
12718
13235
  if (trimmed) {
@@ -12722,9 +13239,12 @@ function normalizeArchitectureResult({
12722
13239
  const parsed = JSON.parse(lastLine);
12723
13240
  return {
12724
13241
  outputRoot: workspaceRoot,
12725
- filesWritten: Array.isArray(parsed.files_written)
12726
- ? parsed.files_written
12727
- : [rulesPath, techPath],
13242
+ filesWritten:
13243
+ changedFiles.length > 0
13244
+ ? changedFiles
13245
+ : Array.isArray(parsed.files_written)
13246
+ ? parsed.files_written
13247
+ : [],
12728
13248
  researchUsed:
12729
13249
  typeof parsed.research_used === "boolean"
12730
13250
  ? parsed.research_used
@@ -12743,7 +13263,7 @@ function normalizeArchitectureResult({
12743
13263
 
12744
13264
  return {
12745
13265
  outputRoot: workspaceRoot,
12746
- filesWritten: [rulesPath, techPath],
13266
+ filesWritten: changedFiles,
12747
13267
  researchUsed: researchMode === "always",
12748
13268
  gaps: [],
12749
13269
  nextActions: [],
@@ -12751,17 +13271,44 @@ function normalizeArchitectureResult({
12751
13271
  };
12752
13272
  }
12753
13273
 
13274
+ async function captureFileContents(filePaths) {
13275
+ const snapshot = {};
13276
+ for (const filePath of filePaths) {
13277
+ if (await pathExists(filePath)) {
13278
+ snapshot[filePath] = await readFile(filePath, "utf8");
13279
+ } else {
13280
+ snapshot[filePath] = null;
13281
+ }
13282
+ }
13283
+ return snapshot;
13284
+ }
13285
+
12754
13286
  async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
13287
+ const specRoots = await listSpecPackRoots(workspaceRoot);
13288
+ const productPath = path.join(workspaceRoot, "PRODUCT.md");
13289
+ const architecturePath = path.join(workspaceRoot, "ARCHITECTURE.md");
12755
13290
  const rulesPath = path.join(workspaceRoot, "ENGINEERING_RULES.md");
12756
13291
  const techPath = path.join(workspaceRoot, "TECH.md");
13292
+ const roadmapPath = path.join(workspaceRoot, "ROADMAP.md");
13293
+ const adrReadmePath = path.join(workspaceRoot, "docs", "adr", "README.md");
12757
13294
  const metadataPath = path.join(
12758
13295
  workspaceRoot,
12759
13296
  ".cbx",
12760
13297
  ARCHITECTURE_BUILD_METADATA_FILENAME,
12761
13298
  );
13299
+ const productExists = await pathExists(productPath);
13300
+ const architectureExists = await pathExists(architecturePath);
12762
13301
  const rulesExists = await pathExists(rulesPath);
12763
13302
  const techExists = await pathExists(techPath);
13303
+ const roadmapExists = await pathExists(roadmapPath);
13304
+ const adrReadmeExists = await pathExists(adrReadmePath);
12764
13305
 
13306
+ const expectedProductHash = hashStableObject(
13307
+ inferProductFoundationProfile(snapshot, specRoots),
13308
+ );
13309
+ const expectedArchitectureHash = hashStableObject(
13310
+ inferArchitectureDocProfile(snapshot, specRoots),
13311
+ );
12765
13312
  const expectedRulesHash = hashStableObject(
12766
13313
  inferArchitectureContractProfile(snapshot),
12767
13314
  );
@@ -12771,10 +13318,54 @@ async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
12771
13318
  frameworks: snapshot.frameworks,
12772
13319
  architectureByApp: snapshot.architectureByApp,
12773
13320
  });
13321
+ const expectedRoadmapHash = hashStableObject({
13322
+ topDirs: snapshot.topDirs,
13323
+ frameworks: snapshot.frameworks,
13324
+ specRoots,
13325
+ });
12774
13326
 
12775
13327
  const findings = [];
13328
+ let actualProductHash = null;
13329
+ let actualArchitectureHash = null;
12776
13330
  let actualRulesHash = null;
12777
13331
  let actualTechHash = null;
13332
+ let actualRoadmapHash = null;
13333
+
13334
+ if (!productExists) {
13335
+ findings.push("PRODUCT.md is missing.");
13336
+ } else {
13337
+ const content = await readFile(productPath, "utf8");
13338
+ actualProductHash = extractTaggedMarkerAttribute(
13339
+ content,
13340
+ PRODUCT_FOUNDATION_BLOCK_START_RE,
13341
+ "profile",
13342
+ );
13343
+ if (!actualProductHash) {
13344
+ findings.push("PRODUCT.md is missing the managed product foundation block.");
13345
+ } else if (actualProductHash !== expectedProductHash) {
13346
+ findings.push(
13347
+ `PRODUCT.md foundation is stale (expected ${expectedProductHash}, found ${actualProductHash}).`,
13348
+ );
13349
+ }
13350
+ }
13351
+
13352
+ if (!architectureExists) {
13353
+ findings.push("ARCHITECTURE.md is missing.");
13354
+ } else {
13355
+ const content = await readFile(architecturePath, "utf8");
13356
+ actualArchitectureHash = extractTaggedMarkerAttribute(
13357
+ content,
13358
+ ARCHITECTURE_DOC_BLOCK_START_RE,
13359
+ "profile",
13360
+ );
13361
+ if (!actualArchitectureHash) {
13362
+ findings.push("ARCHITECTURE.md is missing the managed architecture backbone block.");
13363
+ } else if (actualArchitectureHash !== expectedArchitectureHash) {
13364
+ findings.push(
13365
+ `ARCHITECTURE.md backbone is stale (expected ${expectedArchitectureHash}, found ${actualArchitectureHash}).`,
13366
+ );
13367
+ }
13368
+ }
12778
13369
 
12779
13370
  if (!rulesExists) {
12780
13371
  findings.push("ENGINEERING_RULES.md is missing.");
@@ -12812,6 +13403,28 @@ async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
12812
13403
  }
12813
13404
  }
12814
13405
 
13406
+ if (!roadmapExists) {
13407
+ findings.push("ROADMAP.md is missing.");
13408
+ } else {
13409
+ const content = await readFile(roadmapPath, "utf8");
13410
+ actualRoadmapHash = extractTaggedMarkerAttribute(
13411
+ content,
13412
+ ROADMAP_FOUNDATION_BLOCK_START_RE,
13413
+ "profile",
13414
+ );
13415
+ if (!actualRoadmapHash) {
13416
+ findings.push("ROADMAP.md is missing the managed roadmap foundation block.");
13417
+ } else if (actualRoadmapHash !== expectedRoadmapHash) {
13418
+ findings.push(
13419
+ `ROADMAP.md backbone is stale (expected ${expectedRoadmapHash}, found ${actualRoadmapHash}).`,
13420
+ );
13421
+ }
13422
+ }
13423
+
13424
+ if (!adrReadmeExists) {
13425
+ findings.push("docs/adr/README.md is missing.");
13426
+ }
13427
+
12815
13428
  const metadata = await readJsonFileIfExists(metadataPath);
12816
13429
  if (!metadata.exists) {
12817
13430
  findings.push("Architecture build metadata is missing.");
@@ -12820,13 +13433,23 @@ async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
12820
13433
  return {
12821
13434
  stale: findings.length > 0,
12822
13435
  findings,
13436
+ productPath,
13437
+ architecturePath,
12823
13438
  rulesPath,
12824
13439
  techPath,
13440
+ roadmapPath,
13441
+ adrReadmePath,
12825
13442
  metadataPath,
13443
+ expectedProductHash,
13444
+ expectedArchitectureHash,
12826
13445
  expectedRulesHash,
12827
13446
  expectedTechHash,
13447
+ expectedRoadmapHash,
13448
+ actualProductHash,
13449
+ actualArchitectureHash,
12828
13450
  actualRulesHash,
12829
13451
  actualTechHash,
13452
+ actualRoadmapHash,
12830
13453
  };
12831
13454
  }
12832
13455
 
@@ -12841,6 +13464,7 @@ async function runBuildArchitecture(options) {
12841
13464
  const cwd = process.cwd();
12842
13465
  const workspaceRoot = findWorkspaceRoot(cwd);
12843
13466
  const snapshot = await collectTechSnapshot(workspaceRoot);
13467
+ const specRoots = await listSpecPackRoots(workspaceRoot);
12844
13468
 
12845
13469
  if (checkOnly) {
12846
13470
  const drift = await readArchitectureDriftStatus(workspaceRoot, snapshot);
@@ -12850,6 +13474,9 @@ async function runBuildArchitecture(options) {
12850
13474
  console.log(`Platform: ${platform}`);
12851
13475
  console.log(`Workspace: ${toPosixPath(workspaceRoot)}`);
12852
13476
  console.log(`Status: ${drift.stale ? "stale" : "fresh"}`);
13477
+ console.log(
13478
+ "Backbone docs: PRODUCT.md, ARCHITECTURE.md, ENGINEERING_RULES.md, TECH.md, ROADMAP.md, docs/adr/README.md",
13479
+ );
12853
13480
  if (drift.findings.length > 0) {
12854
13481
  console.log("Findings:");
12855
13482
  for (const finding of drift.findings) {
@@ -12861,13 +13488,26 @@ async function runBuildArchitecture(options) {
12861
13488
  return;
12862
13489
  }
12863
13490
 
13491
+ const managedFilePaths = [
13492
+ path.join(workspaceRoot, "PRODUCT.md"),
13493
+ path.join(workspaceRoot, "ARCHITECTURE.md"),
13494
+ path.join(workspaceRoot, "ENGINEERING_RULES.md"),
13495
+ path.join(workspaceRoot, "TECH.md"),
13496
+ path.join(workspaceRoot, "ROADMAP.md"),
13497
+ path.join(workspaceRoot, "docs", "adr", "README.md"),
13498
+ path.join(workspaceRoot, "docs", "adr", "0000-template.md"),
13499
+ ];
13500
+ const filesBefore = dryRun
13501
+ ? Object.fromEntries(managedFilePaths.map((filePath) => [filePath, null]))
13502
+ : await captureFileContents(managedFilePaths);
13503
+
12864
13504
  const scaffold = await ensureArchitectureDocScaffold({
12865
13505
  workspaceRoot,
12866
13506
  snapshot,
13507
+ specRoots,
12867
13508
  overwrite,
12868
13509
  dryRun,
12869
13510
  });
12870
- const specRoots = await listSpecPackRoots(workspaceRoot);
12871
13511
  const coreSkills = [
12872
13512
  "architecture-doc",
12873
13513
  "system-design",
@@ -12906,8 +13546,13 @@ async function runBuildArchitecture(options) {
12906
13546
  invocation: [adapter.binary, ...args],
12907
13547
  researchMode,
12908
13548
  managedTargets: [
13549
+ toPosixPath(scaffold.productPath),
13550
+ toPosixPath(scaffold.architectureDocPath),
12909
13551
  toPosixPath(scaffold.engineeringRulesPath),
12910
13552
  toPosixPath(scaffold.techMdPath),
13553
+ toPosixPath(scaffold.roadmapPath),
13554
+ toPosixPath(scaffold.adrReadmePath),
13555
+ toPosixPath(scaffold.adrTemplatePath),
12911
13556
  ],
12912
13557
  skillBundle,
12913
13558
  };
@@ -12927,18 +13572,37 @@ async function runBuildArchitecture(options) {
12927
13572
  return;
12928
13573
  }
12929
13574
 
12930
- const execution = await execFileCapture(adapter.binary, args, {
13575
+ if (!emitJson) {
13576
+ console.log(`Streaming ${adapter.binary} output...`);
13577
+ }
13578
+
13579
+ const execution = await spawnCapture(adapter.binary, args, {
12931
13580
  cwd: workspaceRoot,
12932
13581
  env: process.env,
13582
+ streamOutput: !emitJson,
12933
13583
  });
12934
13584
  if (!execution.ok) {
12935
- throw new Error(
12936
- `Architecture build failed via ${adapter.binary}. ${String(execution.stderr || execution.stdout || "").trim()}`,
12937
- );
12938
- }
13585
+ throw new Error(explainArchitectureBuildFailure(platform, execution));
13586
+ }
13587
+
13588
+ const filesAfter = await captureFileContents(managedFilePaths);
13589
+ const changedFiles = managedFilePaths
13590
+ .filter((filePath) => filesBefore[filePath] !== filesAfter[filePath])
13591
+ .map((filePath) => toPosixPath(path.relative(workspaceRoot, filePath)));
13592
+
13593
+ const rulesContent =
13594
+ filesAfter[scaffold.engineeringRulesPath] ??
13595
+ (await readFile(scaffold.engineeringRulesPath, "utf8"));
13596
+ const techContent =
13597
+ filesAfter[scaffold.techMdPath] ?? (await readFile(scaffold.techMdPath, "utf8"));
13598
+ const productContent =
13599
+ filesAfter[scaffold.productPath] ?? (await readFile(scaffold.productPath, "utf8"));
13600
+ const architectureContent =
13601
+ filesAfter[scaffold.architectureDocPath] ??
13602
+ (await readFile(scaffold.architectureDocPath, "utf8"));
13603
+ const roadmapContent =
13604
+ filesAfter[scaffold.roadmapPath] ?? (await readFile(scaffold.roadmapPath, "utf8"));
12939
13605
 
12940
- const rulesContent = await readFile(scaffold.engineeringRulesPath, "utf8");
12941
- const techContent = await readFile(scaffold.techMdPath, "utf8");
12942
13606
  const metadataPath = path.join(
12943
13607
  workspaceRoot,
12944
13608
  ".cbx",
@@ -12947,6 +13611,18 @@ async function runBuildArchitecture(options) {
12947
13611
  const metadata = buildArchitectureBuildMetadata({
12948
13612
  platform,
12949
13613
  researchMode,
13614
+ productProfileHash:
13615
+ extractTaggedMarkerAttribute(
13616
+ productContent,
13617
+ PRODUCT_FOUNDATION_BLOCK_START_RE,
13618
+ "profile",
13619
+ ) || "unknown",
13620
+ architectureDocHash:
13621
+ extractTaggedMarkerAttribute(
13622
+ architectureContent,
13623
+ ARCHITECTURE_DOC_BLOCK_START_RE,
13624
+ "profile",
13625
+ ) || "unknown",
12950
13626
  rulesProfileHash:
12951
13627
  extractTaggedMarkerAttribute(
12952
13628
  rulesContent,
@@ -12959,6 +13635,12 @@ async function runBuildArchitecture(options) {
12959
13635
  TECH_ARCHITECTURE_BLOCK_START_RE,
12960
13636
  "snapshot",
12961
13637
  ) || "unknown",
13638
+ roadmapProfileHash:
13639
+ extractTaggedMarkerAttribute(
13640
+ roadmapContent,
13641
+ ROADMAP_FOUNDATION_BLOCK_START_RE,
13642
+ "profile",
13643
+ ) || "unknown",
12962
13644
  });
12963
13645
  await mkdir(path.dirname(metadataPath), { recursive: true });
12964
13646
  await writeFile(
@@ -12970,9 +13652,8 @@ async function runBuildArchitecture(options) {
12970
13652
  const result = normalizeArchitectureResult({
12971
13653
  stdout: execution.stdout,
12972
13654
  workspaceRoot: toPosixPath(workspaceRoot),
12973
- rulesPath: "ENGINEERING_RULES.md",
12974
- techPath: "TECH.md",
12975
13655
  researchMode,
13656
+ changedFiles: [...new Set(changedFiles)],
12976
13657
  });
12977
13658
 
12978
13659
  if (emitJson) {
@@ -12994,7 +13675,12 @@ async function runBuildArchitecture(options) {
12994
13675
  console.log(`Platform: ${platform}`);
12995
13676
  console.log(`Adapter: ${adapter.binary}`);
12996
13677
  console.log(`Workspace: ${toPosixPath(workspaceRoot)}`);
12997
- console.log(`Managed docs: ENGINEERING_RULES.md, TECH.md`);
13678
+ console.log(
13679
+ "Managed docs: PRODUCT.md, ARCHITECTURE.md, ENGINEERING_RULES.md, TECH.md, ROADMAP.md",
13680
+ );
13681
+ console.log(
13682
+ "ADR scaffold: docs/adr/README.md, docs/adr/0000-template.md",
13683
+ );
12998
13684
  console.log(`Skill bundle: ${skillBundle.join(", ")}`);
12999
13685
  console.log(
13000
13686
  `Files written: ${(result.filesWritten || []).join(", ") || "(none reported)"}`,
@@ -13273,11 +13959,7 @@ async function runInitWizard(options) {
13273
13959
  dryRun: installOutcome.dryRun,
13274
13960
  });
13275
13961
  printRuleSyncResult(installOutcome.syncResult);
13276
- printInstallEngineeringSummary({
13277
- engineeringResults:
13278
- installOutcome.engineeringArtifactsResult.engineeringResults,
13279
- techResult: installOutcome.engineeringArtifactsResult.techResult,
13280
- });
13962
+ printInstallDocumentationNotice();
13281
13963
  printPostmanSetupSummary({
13282
13964
  postmanSetup: installOutcome.postmanSetupResult,
13283
13965
  });