@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/dist/cli/core.js CHANGED
@@ -27,8 +27,14 @@ const ENGINEERING_RULES_FILE_BLOCK_START_RE = /<!--\s*cbx:engineering:rules:star
27
27
  const ENGINEERING_RULES_FILE_BLOCK_END_RE = /<!--\s*cbx:engineering:rules:end\s*-->/g;
28
28
  const ENGINEERING_ARCHITECTURE_BLOCK_START_RE = /<!--\s*cbx:architecture:rules:start[^>]*-->/g;
29
29
  const ENGINEERING_ARCHITECTURE_BLOCK_END_RE = /<!--\s*cbx:architecture:rules:end\s*-->/g;
30
+ const PRODUCT_FOUNDATION_BLOCK_START_RE = /<!--\s*cbx:product:foundation:start[^>]*-->/g;
31
+ const PRODUCT_FOUNDATION_BLOCK_END_RE = /<!--\s*cbx:product:foundation:end\s*-->/g;
32
+ const ARCHITECTURE_DOC_BLOCK_START_RE = /<!--\s*cbx:architecture:doc:start[^>]*-->/g;
33
+ const ARCHITECTURE_DOC_BLOCK_END_RE = /<!--\s*cbx:architecture:doc:end\s*-->/g;
30
34
  const TECH_ARCHITECTURE_BLOCK_START_RE = /<!--\s*cbx:architecture:tech:start[^>]*-->/g;
31
35
  const TECH_ARCHITECTURE_BLOCK_END_RE = /<!--\s*cbx:architecture:tech:end\s*-->/g;
36
+ const ROADMAP_FOUNDATION_BLOCK_START_RE = /<!--\s*cbx:roadmap:foundation:start[^>]*-->/g;
37
+ const ROADMAP_FOUNDATION_BLOCK_END_RE = /<!--\s*cbx:roadmap:foundation:end\s*-->/g;
32
38
  const COPILOT_ALLOWED_SKILL_FRONTMATTER_KEYS = new Set([
33
39
  "compatibility",
34
40
  "description",
@@ -954,6 +960,154 @@ function buildArchitectureMermaid(snapshot) {
954
960
  }
955
961
  return lines.join("\n");
956
962
  }
963
+ function inferProductFoundationProfile(snapshot, specRoots = []) {
964
+ const appRoots = (snapshot.architectureByApp || [])
965
+ .map((item) => item.rootPath)
966
+ .filter((value) => value && value !== ".");
967
+ const primarySurfaces = appRoots.length > 0 ? appRoots : snapshot.topDirs.slice(0, 6);
968
+ const userPersonas = [];
969
+ if (snapshot.frameworks.includes("Flutter")) {
970
+ userPersonas.push("End users interacting through mobile or desktop app surfaces");
971
+ }
972
+ if (snapshot.frameworks.includes("Next.js") ||
973
+ snapshot.frameworks.includes("React") ||
974
+ snapshot.topDirs.includes("web")) {
975
+ userPersonas.push("Browser users and internal operators using web-facing flows");
976
+ }
977
+ if (snapshot.frameworks.includes("NestJS") || snapshot.topDirs.includes("api")) {
978
+ userPersonas.push("Internal services, operators, or partner systems consuming API boundaries");
979
+ }
980
+ if (userPersonas.length === 0) {
981
+ userPersonas.push("Project stakeholders are not obvious from the repo alone; refine personas from product context before major feature work.");
982
+ }
983
+ const coreJourneys = [];
984
+ if (specRoots.length > 0) {
985
+ coreJourneys.push(`Active implementation themes are reflected in current spec packs: ${specRoots.join(", ")}.`);
986
+ }
987
+ if (primarySurfaces.length > 0) {
988
+ coreJourneys.push(`Primary product surfaces currently live in: ${primarySurfaces.join(", ")}.`);
989
+ }
990
+ if (snapshot.isMcpServer || snapshot.mcpSignals.length > 0) {
991
+ coreJourneys.push("Tool-assisted and MCP-driven workflows are part of the operating model and should stay stable.");
992
+ }
993
+ if (coreJourneys.length === 0) {
994
+ coreJourneys.push("Repository evidence is thin; capture the primary user journeys here before scaling the codebase further.");
995
+ }
996
+ const successSignals = [
997
+ "Feature work should stay aligned to explicit user or operator value, not speculative abstractions.",
998
+ "Architecture changes should reduce onboarding, debugging, and testing cost over time.",
999
+ ];
1000
+ if (snapshot.cicdSignals.length > 0) {
1001
+ successSignals.push(`Delivery flows already rely on ${snapshot.cicdSignals.join(", ")} signals; keep release friction low for that pipeline.`);
1002
+ }
1003
+ return {
1004
+ productScope: snapshot.readmeExcerpt ||
1005
+ "No explicit product summary was detected from repo docs. Replace this with a concise product statement when better context exists.",
1006
+ primarySurfaces,
1007
+ userPersonas,
1008
+ coreJourneys,
1009
+ successSignals,
1010
+ };
1011
+ }
1012
+ function buildProductFoundationSection(snapshot, specRoots = []) {
1013
+ const profile = inferProductFoundationProfile(snapshot, specRoots);
1014
+ const hash = hashStableObject(profile);
1015
+ return [
1016
+ `<!-- cbx:product:foundation:start version=1 profile=${hash} -->`,
1017
+ "## Product Foundation (auto-managed)",
1018
+ "",
1019
+ "### Product Scope",
1020
+ profile.productScope,
1021
+ "",
1022
+ "### Primary Surfaces",
1023
+ ...(profile.primarySurfaces.length > 0
1024
+ ? profile.primarySurfaces.map((item) => `- ${item}`)
1025
+ : ["- No primary surfaces were detected automatically."]),
1026
+ "",
1027
+ "### Users and Operators",
1028
+ ...profile.userPersonas.map((item) => `- ${item}`),
1029
+ "",
1030
+ "### Core Journeys",
1031
+ ...profile.coreJourneys.map((item) => `- ${item}`),
1032
+ "",
1033
+ "### Success Signals",
1034
+ ...profile.successSignals.map((item) => `- ${item}`),
1035
+ "",
1036
+ "### Product Guardrails",
1037
+ "- Keep product intent stable across future features so agents do not optimize for the wrong user outcome.",
1038
+ "- Refresh this managed section when scope, personas, operating model, or success metrics change materially.",
1039
+ "<!-- cbx:product:foundation:end -->",
1040
+ "",
1041
+ ].join("\n");
1042
+ }
1043
+ function inferArchitectureDocProfile(snapshot, specRoots = []) {
1044
+ const contract = inferArchitectureContractProfile(snapshot);
1045
+ const architectureSignals = (snapshot.architectureByApp || []).map((item) => {
1046
+ const label = item.rootPath === "." ? "repo root" : item.rootPath;
1047
+ const signals = item.architectureSignals && item.architectureSignals.length > 0
1048
+ ? item.architectureSignals.join(", ")
1049
+ : "not enough signals to classify";
1050
+ return `${label}: ${signals}`;
1051
+ });
1052
+ const decisionAreas = [
1053
+ "Module boundaries and dependency direction",
1054
+ "Design-system ownership and reusable primitives",
1055
+ "Testing and validation expectations by runtime boundary",
1056
+ ];
1057
+ if (specRoots.length > 0) {
1058
+ decisionAreas.push(`Active specs that may influence upcoming architecture work: ${specRoots.join(", ")}.`);
1059
+ }
1060
+ return {
1061
+ style: contract.style,
1062
+ designSystemSource: contract.designSystemSource,
1063
+ moduleBoundaries: contract.moduleBoundaries,
1064
+ architectureSignals,
1065
+ decisionAreas,
1066
+ scalingConstraints: contract.scalingConstraints,
1067
+ testingStrategy: contract.testingStrategy,
1068
+ };
1069
+ }
1070
+ function buildArchitectureDocSection(snapshot, specRoots = []) {
1071
+ const profile = inferArchitectureDocProfile(snapshot, specRoots);
1072
+ const hash = hashStableObject(profile);
1073
+ return [
1074
+ `<!-- cbx:architecture:doc:start version=1 profile=${hash} -->`,
1075
+ "## Accepted Architecture Backbone (auto-managed)",
1076
+ "",
1077
+ "### System Overview",
1078
+ `- Accepted style: ${profile.style}.`,
1079
+ `- Design-system source of truth: ${profile.designSystemSource}`,
1080
+ "",
1081
+ "### Bounded Contexts and Module Boundaries",
1082
+ ...(profile.moduleBoundaries.length > 0
1083
+ ? profile.moduleBoundaries.map((item) => `- ${item}`)
1084
+ : ["- No strong top-level module boundaries were detected automatically."]),
1085
+ "",
1086
+ "### Architecture Signals by Surface",
1087
+ ...(profile.architectureSignals.length > 0
1088
+ ? profile.architectureSignals.map((item) => `- ${item}`)
1089
+ : ["- No app-level architecture signals were inferred from the repo scan."]),
1090
+ "",
1091
+ "### Decision Areas to Preserve",
1092
+ ...profile.decisionAreas.map((item) => `- ${item}`),
1093
+ "",
1094
+ "### Scalability and Reliability Notes",
1095
+ ...profile.scalingConstraints.map((item) => `- ${item}`),
1096
+ "",
1097
+ "### Testing and Operability",
1098
+ ...profile.testingStrategy.map((item) => `- ${item}`),
1099
+ "",
1100
+ "### ADR Linkage",
1101
+ "- Keep durable architecture decisions in `docs/adr/` and summarize the active decision set here.",
1102
+ "",
1103
+ "### Mermaid Diagram",
1104
+ "```mermaid",
1105
+ buildArchitectureDocMermaid(snapshot),
1106
+ "```",
1107
+ "<!-- cbx:architecture:doc:end -->",
1108
+ "",
1109
+ ].join("\n");
1110
+ }
957
1111
  function buildEngineeringArchitectureSection(snapshot) {
958
1112
  const profile = inferArchitectureContractProfile(snapshot);
959
1113
  const hash = hashStableObject(profile);
@@ -973,11 +1127,30 @@ function buildEngineeringArchitectureSection(snapshot) {
973
1127
  ...profile.testingStrategy.map((rule) => ` - ${rule}`),
974
1128
  "- Doc refresh policy:",
975
1129
  " - Update these managed sections when architecture, scale, boundaries, design-system rules, or testing strategy changes.",
976
- " - For non-trivial work, read this file before planning and read TECH.md next.",
1130
+ " - For non-trivial work, read PRODUCT.md, ENGINEERING_RULES.md, ARCHITECTURE.md, and TECH.md in that order when they exist.",
977
1131
  "<!-- cbx:architecture:rules:end -->",
978
1132
  "",
979
1133
  ].join("\n");
980
1134
  }
1135
+ function buildArchitectureDocMermaid(snapshot) {
1136
+ const topDirs = snapshot.topDirs.slice(0, 5);
1137
+ const lines = [
1138
+ "flowchart LR",
1139
+ ' product["Product Intent"] --> rules["Engineering Rules"]',
1140
+ ' product --> arch["Architecture Backbone"]',
1141
+ ' arch --> tech["Current Tech Snapshot"]',
1142
+ ];
1143
+ if (topDirs.length > 0) {
1144
+ for (let index = 0; index < topDirs.length; index += 1) {
1145
+ const dir = topDirs[index];
1146
+ const nodeName = `D${index}`;
1147
+ lines.push(` arch --> ${nodeName}["${dir}/"]`);
1148
+ }
1149
+ }
1150
+ lines.push(' arch --> adr["docs/adr"]');
1151
+ lines.push(' product --> specs["docs/specs"]');
1152
+ return lines.join("\n");
1153
+ }
981
1154
  function buildTechArchitectureSection(snapshot) {
982
1155
  const profile = inferArchitectureContractProfile(snapshot);
983
1156
  const payload = {
@@ -1030,6 +1203,48 @@ function buildTechArchitectureSection(snapshot) {
1030
1203
  "",
1031
1204
  ].join("\n");
1032
1205
  }
1206
+ function buildRoadmapFoundationSection(snapshot, specRoots = []) {
1207
+ const payload = {
1208
+ topDirs: snapshot.topDirs,
1209
+ frameworks: snapshot.frameworks,
1210
+ specRoots,
1211
+ };
1212
+ const hash = hashStableObject(payload);
1213
+ const nowItems = specRoots.length > 0
1214
+ ? specRoots.map((item) => `Track active change planning in \`${item}\`.`)
1215
+ : [
1216
+ "No active spec packs detected. Create a spec pack before starting the next non-trivial feature or migration.",
1217
+ ];
1218
+ const nextItems = [];
1219
+ if (snapshot.frameworks.length > 0) {
1220
+ nextItems.push(`Keep shared conventions stable across the current stack: ${snapshot.frameworks.join(", ")}.`);
1221
+ }
1222
+ if (snapshot.cicdSignals.length > 0) {
1223
+ nextItems.push(`Preserve release compatibility with the detected delivery surfaces: ${snapshot.cicdSignals.join(", ")}.`);
1224
+ }
1225
+ if (nextItems.length === 0) {
1226
+ nextItems.push("Document the next scaling milestones here once product direction and architecture constraints are clearer.");
1227
+ }
1228
+ return [
1229
+ `<!-- cbx:roadmap:foundation:start version=1 profile=${hash} -->`,
1230
+ "## Delivery Backbone (auto-managed)",
1231
+ "",
1232
+ "### Now",
1233
+ ...nowItems.map((item) => `- ${item}`),
1234
+ "",
1235
+ "### Next",
1236
+ ...nextItems.map((item) => `- ${item}`),
1237
+ "",
1238
+ "### Later",
1239
+ "- Use this section for medium-term scaling themes, major migrations, or cross-team architecture investments.",
1240
+ "",
1241
+ "### Backbone Maintenance",
1242
+ "- Keep PRODUCT.md, ARCHITECTURE.md, ENGINEERING_RULES.md, and TECH.md aligned when direction or structure changes.",
1243
+ "- Link major roadmap themes back to specs and ADRs instead of burying them in chat-only planning.",
1244
+ "<!-- cbx:roadmap:foundation:end -->",
1245
+ "",
1246
+ ].join("\n");
1247
+ }
1033
1248
  function buildEngineeringRulesTemplate() {
1034
1249
  return [
1035
1250
  "# Engineering Rules",
@@ -1131,17 +1346,103 @@ function buildEngineeringRulesTemplate() {
1131
1346
  "",
1132
1347
  ].join("\n");
1133
1348
  }
1134
- function buildEngineeringRulesManagedBlock({ platform, engineeringRulesFilePath, techMdFilePath, ruleFilePath, }) {
1349
+ function buildProductTemplate(snapshot, specRoots = []) {
1350
+ return [
1351
+ "# Product",
1352
+ "",
1353
+ "This file is the durable product backbone for the project.",
1354
+ "",
1355
+ buildProductFoundationSection(snapshot, specRoots).trimEnd(),
1356
+ "",
1357
+ ].join("\n");
1358
+ }
1359
+ function buildArchitectureDocTemplate(snapshot, specRoots = []) {
1360
+ return [
1361
+ "# Architecture",
1362
+ "",
1363
+ "This file captures the accepted architecture backbone for the project.",
1364
+ "",
1365
+ buildArchitectureDocSection(snapshot, specRoots).trimEnd(),
1366
+ "",
1367
+ ].join("\n");
1368
+ }
1369
+ function buildRoadmapTemplate(snapshot, specRoots = []) {
1370
+ return [
1371
+ "# Roadmap",
1372
+ "",
1373
+ "This file captures the living delivery backbone for medium-term product and architecture work.",
1374
+ "",
1375
+ buildRoadmapFoundationSection(snapshot, specRoots).trimEnd(),
1376
+ "",
1377
+ ].join("\n");
1378
+ }
1379
+ function buildAdrReadme() {
1380
+ return [
1381
+ "# Architecture Decision Records",
1382
+ "",
1383
+ "Use this directory for durable decisions that future contributors and agents need to preserve.",
1384
+ "",
1385
+ "## When to add an ADR",
1386
+ "",
1387
+ "- Architecture style or boundary changes",
1388
+ "- Data model or persistence strategy changes",
1389
+ "- Deployment or scaling model changes",
1390
+ "- Design-system ownership or shared UX pattern changes",
1391
+ "",
1392
+ "## Suggested format",
1393
+ "",
1394
+ "1. Context",
1395
+ "2. Decision",
1396
+ "3. Consequences",
1397
+ "4. Validation",
1398
+ "",
1399
+ "Start with `0000-template.md` and create numbered follow-up ADRs for accepted decisions.",
1400
+ "",
1401
+ ].join("\n");
1402
+ }
1403
+ function buildAdrTemplate() {
1404
+ return [
1405
+ "# ADR 0000: Title",
1406
+ "",
1407
+ "## Status",
1408
+ "",
1409
+ "Proposed",
1410
+ "",
1411
+ "## Context",
1412
+ "",
1413
+ "- What problem or pressure led to this decision?",
1414
+ "",
1415
+ "## Decision",
1416
+ "",
1417
+ "- What is the chosen direction?",
1418
+ "",
1419
+ "## Consequences",
1420
+ "",
1421
+ "- What tradeoffs, benefits, or costs follow from this choice?",
1422
+ "",
1423
+ "## Validation",
1424
+ "",
1425
+ "- How will the team know this decision is working?",
1426
+ "",
1427
+ ].join("\n");
1428
+ }
1429
+ function buildEngineeringRulesManagedBlock({ platform, productFilePath, architectureFilePath, engineeringRulesFilePath, techMdFilePath, roadmapFilePath, ruleFilePath, }) {
1430
+ const productRef = toPosixPath(path.resolve(productFilePath));
1431
+ const architectureRef = toPosixPath(path.resolve(architectureFilePath));
1135
1432
  const engineeringRef = toPosixPath(path.resolve(engineeringRulesFilePath));
1136
1433
  const techRef = toPosixPath(path.resolve(techMdFilePath));
1434
+ const roadmapRef = toPosixPath(path.resolve(roadmapFilePath));
1137
1435
  const ruleRef = toPosixPath(path.resolve(ruleFilePath));
1138
1436
  return [
1139
1437
  `<!-- cbx:engineering:auto:start platform=${platform} version=1 -->`,
1140
1438
  "## Engineering Guardrails (auto-managed)",
1141
1439
  "Apply these before planning, coding, review, and release:",
1142
1440
  "",
1441
+ `- Product backbone: \`${productRef}\``,
1442
+ `- Accepted architecture: \`${architectureRef}\``,
1143
1443
  `- Required baseline: \`${engineeringRef}\``,
1144
1444
  `- Project tech map: \`${techRef}\``,
1445
+ `- Delivery roadmap: \`${roadmapRef}\``,
1145
1446
  `- Active platform rule file: \`${ruleRef}\``,
1146
1447
  "",
1147
1448
  "Hard policy:",
@@ -1149,7 +1450,7 @@ function buildEngineeringRulesManagedBlock({ platform, engineeringRulesFilePath,
1149
1450
  "2. Keep architecture simple (KISS) and avoid speculative work (YAGNI).",
1150
1451
  "3. Apply SOLID pragmatically to reduce change risk, not add ceremony.",
1151
1452
  "4. Use clear naming with focused responsibilities and explicit boundaries.",
1152
- "5. For non-trivial work, read ENGINEERING_RULES.md first and TECH.md next before planning or implementation.",
1453
+ "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.",
1153
1454
  "6. Require validation evidence (lint/types/tests) before merge.",
1154
1455
  "7. Use Decision Log response style.",
1155
1456
  "8. Every Decision Log must include a `Skills Used` section listing skill, workflow, or agent names.",
@@ -1240,11 +1541,14 @@ async function upsertEngineeringRulesFile({ targetPath, template, overwrite = fa
1240
1541
  filePath: targetPath,
1241
1542
  };
1242
1543
  }
1243
- async function upsertEngineeringRulesBlock({ ruleFilePath, platform, engineeringRulesFilePath, techMdFilePath, dryRun = false, }) {
1544
+ async function upsertEngineeringRulesBlock({ ruleFilePath, platform, productFilePath, architectureFilePath, engineeringRulesFilePath, techMdFilePath, roadmapFilePath, dryRun = false, }) {
1244
1545
  const block = buildEngineeringRulesManagedBlock({
1245
1546
  platform,
1547
+ productFilePath,
1548
+ architectureFilePath,
1246
1549
  engineeringRulesFilePath,
1247
1550
  techMdFilePath,
1551
+ roadmapFilePath,
1248
1552
  ruleFilePath,
1249
1553
  });
1250
1554
  const exists = await pathExists(ruleFilePath);
@@ -1334,20 +1638,45 @@ async function upsertTaggedSectionInFile({ targetPath, initialContent, block, st
1334
1638
  filePath: targetPath,
1335
1639
  };
1336
1640
  }
1337
- function buildArchitectureBuildMetadata({ platform, researchMode, rulesProfileHash, techSnapshotHash, }) {
1641
+ function buildArchitectureBuildMetadata({ platform, researchMode, productProfileHash, architectureDocHash, rulesProfileHash, techSnapshotHash, roadmapProfileHash, }) {
1338
1642
  return {
1339
- schemaVersion: 1,
1643
+ schemaVersion: 2,
1340
1644
  generatedBy: "cbx build architecture",
1341
1645
  generatedAt: new Date().toISOString(),
1342
1646
  platform,
1343
1647
  researchMode,
1648
+ productProfileHash,
1649
+ architectureDocHash,
1344
1650
  rulesProfileHash,
1345
1651
  techSnapshotHash,
1652
+ roadmapProfileHash,
1346
1653
  };
1347
1654
  }
1348
- async function ensureArchitectureDocScaffold({ workspaceRoot, snapshot, overwrite = false, dryRun = false, }) {
1655
+ async function ensureArchitectureDocScaffold({ workspaceRoot, snapshot, specRoots = [], overwrite = false, dryRun = false, }) {
1656
+ const productPath = path.join(workspaceRoot, "PRODUCT.md");
1657
+ const architectureDocPath = path.join(workspaceRoot, "ARCHITECTURE.md");
1349
1658
  const engineeringRulesPath = path.join(workspaceRoot, "ENGINEERING_RULES.md");
1350
1659
  const techMdPath = path.join(workspaceRoot, "TECH.md");
1660
+ const roadmapPath = path.join(workspaceRoot, "ROADMAP.md");
1661
+ const adrDir = path.join(workspaceRoot, "docs", "adr");
1662
+ const adrReadmePath = path.join(adrDir, "README.md");
1663
+ const adrTemplatePath = path.join(adrDir, "0000-template.md");
1664
+ const productResult = await upsertTaggedSectionInFile({
1665
+ targetPath: productPath,
1666
+ initialContent: `${buildProductTemplate(snapshot, specRoots)}\n`,
1667
+ block: buildProductFoundationSection(snapshot, specRoots),
1668
+ startPattern: PRODUCT_FOUNDATION_BLOCK_START_RE,
1669
+ endPattern: PRODUCT_FOUNDATION_BLOCK_END_RE,
1670
+ dryRun,
1671
+ });
1672
+ const architectureDocResult = await upsertTaggedSectionInFile({
1673
+ targetPath: architectureDocPath,
1674
+ initialContent: `${buildArchitectureDocTemplate(snapshot, specRoots)}\n`,
1675
+ block: buildArchitectureDocSection(snapshot, specRoots),
1676
+ startPattern: ARCHITECTURE_DOC_BLOCK_START_RE,
1677
+ endPattern: ARCHITECTURE_DOC_BLOCK_END_RE,
1678
+ dryRun,
1679
+ });
1351
1680
  const rulesTemplate = buildEngineeringRulesTemplate();
1352
1681
  const rulesFileResult = await upsertEngineeringRulesFile({
1353
1682
  targetPath: engineeringRulesPath,
@@ -1378,13 +1707,43 @@ async function ensureArchitectureDocScaffold({ workspaceRoot, snapshot, overwrit
1378
1707
  endPattern: TECH_ARCHITECTURE_BLOCK_END_RE,
1379
1708
  dryRun,
1380
1709
  });
1710
+ const roadmapResult = await upsertTaggedSectionInFile({
1711
+ targetPath: roadmapPath,
1712
+ initialContent: `${buildRoadmapTemplate(snapshot, specRoots)}\n`,
1713
+ block: buildRoadmapFoundationSection(snapshot, specRoots),
1714
+ startPattern: ROADMAP_FOUNDATION_BLOCK_START_RE,
1715
+ endPattern: ROADMAP_FOUNDATION_BLOCK_END_RE,
1716
+ dryRun,
1717
+ });
1718
+ const adrReadmeResult = await writeTextFile({
1719
+ targetPath: adrReadmePath,
1720
+ content: `${buildAdrReadme()}\n`,
1721
+ overwrite,
1722
+ dryRun,
1723
+ });
1724
+ const adrTemplateResult = await writeTextFile({
1725
+ targetPath: adrTemplatePath,
1726
+ content: `${buildAdrTemplate()}\n`,
1727
+ overwrite,
1728
+ dryRun,
1729
+ });
1381
1730
  return {
1731
+ productPath,
1732
+ architectureDocPath,
1382
1733
  engineeringRulesPath,
1383
1734
  techMdPath,
1735
+ roadmapPath,
1736
+ adrReadmePath,
1737
+ adrTemplatePath,
1738
+ productResult,
1739
+ architectureDocResult,
1384
1740
  rulesFileResult,
1385
1741
  rulesArchitectureResult,
1386
1742
  techResult,
1387
1743
  techArchitectureResult,
1744
+ roadmapResult,
1745
+ adrReadmeResult,
1746
+ adrTemplateResult,
1388
1747
  };
1389
1748
  }
1390
1749
  function normalizeTechPackageName(value) {
@@ -6958,14 +7317,6 @@ async function performWorkflowInstall(options, { postmanSelectionOverride = null
6958
7317
  dryRun,
6959
7318
  cwd,
6960
7319
  });
6961
- const engineeringArtifactsResult = await upsertEngineeringArtifacts({
6962
- platform,
6963
- scope: ruleScope,
6964
- overwrite: false,
6965
- dryRun,
6966
- skipTech: false,
6967
- cwd,
6968
- });
6969
7320
  const postmanSetupResult = await configurePostmanInstallArtifacts({
6970
7321
  platform,
6971
7322
  scope,
@@ -7007,7 +7358,7 @@ async function performWorkflowInstall(options, { postmanSelectionOverride = null
7007
7358
  bundleId,
7008
7359
  installResult,
7009
7360
  syncResult,
7010
- engineeringArtifactsResult,
7361
+ engineeringArtifactsResult: null,
7011
7362
  postmanSetupResult,
7012
7363
  terminalVerificationRuleResult,
7013
7364
  };
@@ -7035,10 +7386,7 @@ async function runWorkflowInstall(options) {
7035
7386
  dryRun: result.dryRun,
7036
7387
  });
7037
7388
  printRuleSyncResult(result.syncResult);
7038
- printInstallEngineeringSummary({
7039
- engineeringResults: result.engineeringArtifactsResult.engineeringResults,
7040
- techResult: result.engineeringArtifactsResult.techResult,
7041
- });
7389
+ printInstallDocumentationNotice();
7042
7390
  printPostmanSetupSummary({
7043
7391
  postmanSetup: result.postmanSetupResult,
7044
7392
  });
@@ -9897,6 +10245,11 @@ function printInstallEngineeringSummary({ engineeringResults, techResult }) {
9897
10245
  console.log(`- TECH scan files: ${techResult.snapshot.scannedFiles}`);
9898
10246
  }
9899
10247
  }
10248
+ function printInstallDocumentationNotice() {
10249
+ console.log("\nProject backbone docs:");
10250
+ console.log("- Install only wires the rule references and workflow assets.");
10251
+ console.log("- 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.");
10252
+ }
9900
10253
  async function upsertEngineeringArtifacts({ platform, scope, overwrite = false, skipTech = false, dryRun = false, cwd = process.cwd(), }) {
9901
10254
  const ruleFilePath = await resolveRuleFilePath(platform, scope, cwd);
9902
10255
  if (!ruleFilePath)
@@ -9906,10 +10259,10 @@ async function upsertEngineeringArtifacts({ platform, scope, overwrite = false,
9906
10259
  const scaffold = await ensureArchitectureDocScaffold({
9907
10260
  workspaceRoot,
9908
10261
  snapshot,
10262
+ specRoots: [],
9909
10263
  overwrite,
9910
10264
  dryRun,
9911
10265
  });
9912
- const techMdPath = path.join(workspaceRoot, "TECH.md");
9913
10266
  const targets = [{ ruleFilePath }];
9914
10267
  if (scope === "global") {
9915
10268
  const workspaceRuleFile = await resolveWorkspaceRuleFileForGlobalScope(platform, cwd);
@@ -9924,8 +10277,11 @@ async function upsertEngineeringArtifacts({ platform, scope, overwrite = false,
9924
10277
  const blockResult = await upsertEngineeringRulesBlock({
9925
10278
  ruleFilePath: target.ruleFilePath,
9926
10279
  platform,
10280
+ productFilePath: scaffold.productPath,
10281
+ architectureFilePath: scaffold.architectureDocPath,
9927
10282
  engineeringRulesFilePath: scaffold.engineeringRulesPath,
9928
- techMdFilePath: techMdPath,
10283
+ techMdFilePath: scaffold.techMdPath,
10284
+ roadmapFilePath: scaffold.roadmapPath,
9929
10285
  dryRun,
9930
10286
  });
9931
10287
  engineeringResults.push({
@@ -10088,8 +10444,12 @@ async function resolveArchitectureSkillPathHints(platform, cwd, skillIds) {
10088
10444
  .map((filePath) => toPosixPath(path.relative(findWorkspaceRoot(cwd), filePath)));
10089
10445
  }
10090
10446
  function buildArchitecturePrompt({ platform, workspaceRoot, snapshot, specRoots, researchMode, coreSkills, conditionalSkills, skillPathHints, }) {
10447
+ const productPath = "PRODUCT.md";
10448
+ const architecturePath = "ARCHITECTURE.md";
10091
10449
  const rulesPath = "ENGINEERING_RULES.md";
10092
10450
  const techPath = "TECH.md";
10451
+ const roadmapPath = "ROADMAP.md";
10452
+ const adrReadmePath = "docs/adr/README.md";
10093
10453
  const architectureSignals = snapshot.architectureByApp
10094
10454
  .filter((item) => (item.architectureSignals || []).length > 0)
10095
10455
  .map((item) => {
@@ -10100,9 +10460,9 @@ function buildArchitecturePrompt({ platform, workspaceRoot, snapshot, specRoots,
10100
10460
  `You are running inside ${platform}.`,
10101
10461
  "",
10102
10462
  "Objective:",
10103
- `- Inspect the repository at ${toPosixPath(workspaceRoot)} and refresh the managed architecture sections in ${rulesPath} and ${techPath}.`,
10104
- "- Keep ENGINEERING_RULES.md normative and TECH.md descriptive.",
10105
- "- Preserve manual content outside the managed `cbx:architecture:*` markers.",
10463
+ `- Inspect the repository at ${toPosixPath(workspaceRoot)} and refresh the scalable project backbone in ${productPath}, ${architecturePath}, ${rulesPath}, ${techPath}, and ${roadmapPath}.`,
10464
+ "- 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.",
10465
+ "- Preserve manual content outside the managed `cbx:*` markers.",
10106
10466
  "",
10107
10467
  "Required skill bundle:",
10108
10468
  `- Load these exact skill IDs first: ${coreSkills.map((skillId) => `\`${skillId}\``).join(", ")}`,
@@ -10123,22 +10483,25 @@ function buildArchitecturePrompt({ platform, workspaceRoot, snapshot, specRoots,
10123
10483
  : "- Architecture signals: none confidently inferred from the repo scan",
10124
10484
  "",
10125
10485
  "Execution contract:",
10126
- "1. Read ENGINEERING_RULES.md first if present, then read TECH.md.",
10486
+ `1. Read ${productPath}, ${rulesPath}, ${architecturePath}, and ${techPath} in that order when they exist.`,
10127
10487
  "2. Inspect the repo before making architecture claims.",
10128
- "3. Update only the content between the existing `cbx:architecture:rules` and `cbx:architecture:tech` markers.",
10129
- "4. Keep the marker lines themselves intact, including their hash metadata.",
10130
- "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.",
10131
- "6. In TECH.md, update architecture snapshot, module or app topology, flow narratives, Mermaid diagrams, and scaling or deployment notes.",
10488
+ "3. Update only the content between the existing managed markers in the backbone docs and preserve the marker lines themselves, including their hash metadata.",
10489
+ "4. In PRODUCT.md, state product scope, primary surfaces, users or operators, core journeys, success signals, and product guardrails.",
10490
+ "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.",
10491
+ "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.",
10492
+ "7. In TECH.md, update architecture snapshot, module or app topology, flow narratives, Mermaid diagrams, and scaling or deployment notes.",
10493
+ "8. In ROADMAP.md, capture current delivery themes, active spec-driven work, and major architecture follow-ups without turning it into a speculative wishlist.",
10132
10494
  researchMode === "never"
10133
- ? "7. Stay repo-only. Do not use outside research."
10134
- : "7. Use repo evidence first. Use official docs when needed. Treat Reddit or community sources only as labeled secondary evidence.",
10495
+ ? "9. Stay repo-only. Do not use outside research."
10496
+ : "9. Use repo evidence first. Use official docs when needed. Treat Reddit or community sources only as labeled secondary evidence.",
10135
10497
  researchMode === "always"
10136
- ? "8. Include an external research evidence subsection in TECH.md with clearly labeled primary and secondary evidence."
10137
- : "8. Include external research notes only if they materially informed the architecture update.",
10138
- "9. If the project clearly follows Clean Architecture, feature-first modules, or another stable structure, make that explicit so future implementation stays consistent.",
10498
+ ? "10. Include an external research evidence subsection in TECH.md with clearly labeled primary and secondary evidence."
10499
+ : "10. Include external research notes only if they materially informed the architecture update.",
10500
+ "11. If the project clearly follows Clean Architecture, feature-first modules, or another stable structure, make that explicit so future implementation stays consistent.",
10501
+ `12. Ensure ${adrReadmePath} exists as the ADR entrypoint and mention ADR follow-up when the repo lacks decision history.`,
10139
10502
  "",
10140
10503
  "Return one JSON object on the last line with this shape:",
10141
- '{"files_written":["ENGINEERING_RULES.md","TECH.md"],"research_used":false,"gaps":[],"next_actions":[]}',
10504
+ '{"files_written":["PRODUCT.md","ARCHITECTURE.md","ENGINEERING_RULES.md","TECH.md","ROADMAP.md","docs/adr/README.md"],"research_used":false,"gaps":[],"next_actions":[]}',
10142
10505
  "",
10143
10506
  "Do not emit placeholder TODOs in the managed sections.",
10144
10507
  ].join("\n");
@@ -10167,6 +10530,73 @@ async function execFileCapture(command, args, options = {}) {
10167
10530
  };
10168
10531
  }
10169
10532
  }
10533
+ async function spawnCapture(command, args, options = {}) {
10534
+ const { cwd, env, streamOutput = false } = options;
10535
+ return await new Promise((resolve, reject) => {
10536
+ let stdout = "";
10537
+ let stderr = "";
10538
+ const child = spawn(command, args, {
10539
+ cwd,
10540
+ env,
10541
+ stdio: ["ignore", "pipe", "pipe"],
10542
+ });
10543
+ child.stdout.on("data", (chunk) => {
10544
+ const text = chunk.toString();
10545
+ stdout += text;
10546
+ if (streamOutput)
10547
+ process.stdout.write(text);
10548
+ });
10549
+ child.stderr.on("data", (chunk) => {
10550
+ const text = chunk.toString();
10551
+ stderr += text;
10552
+ if (streamOutput)
10553
+ process.stderr.write(text);
10554
+ });
10555
+ child.on("error", (error) => {
10556
+ if (error?.code === "ENOENT") {
10557
+ reject(new Error(`Required CLI '${command}' is not installed or not on PATH.`));
10558
+ return;
10559
+ }
10560
+ reject(error);
10561
+ });
10562
+ child.on("close", (code) => {
10563
+ resolve({
10564
+ ok: code === 0,
10565
+ stdout,
10566
+ stderr,
10567
+ code: code ?? 1,
10568
+ });
10569
+ });
10570
+ });
10571
+ }
10572
+ function explainArchitectureBuildFailure(platform, execution) {
10573
+ const combined = String(`${execution.stderr || ""}\n${execution.stdout || ""}`.trim());
10574
+ const notes = [];
10575
+ if (platform === "gemini") {
10576
+ if (combined.includes("Error during discovery for MCP server") ||
10577
+ combined.includes("[MCP error]")) {
10578
+ notes.push("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.");
10579
+ }
10580
+ if (combined.includes("cloudaicompanion.companions.generateChat") ||
10581
+ combined.includes("PERMISSION_DENIED") ||
10582
+ combined.includes("403")) {
10583
+ notes.push("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.");
10584
+ }
10585
+ }
10586
+ if (platform === "claude" && combined.includes("permission")) {
10587
+ notes.push("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.");
10588
+ }
10589
+ if (notes.length === 0) {
10590
+ return `Architecture build failed via ${platform}. ${combined}`.trim();
10591
+ }
10592
+ return [
10593
+ `Architecture build failed via ${platform}.`,
10594
+ ...notes.map((note) => `- ${note}`),
10595
+ combined ? `Raw CLI output:\n${combined}` : "",
10596
+ ]
10597
+ .filter(Boolean)
10598
+ .join("\n");
10599
+ }
10170
10600
  async function probeArchitectureAdapter(platform, cwd) {
10171
10601
  if (platform === "codex") {
10172
10602
  const help = await execFileCapture("codex", ["exec", "--help"], { cwd });
@@ -10242,7 +10672,7 @@ async function probeArchitectureAdapter(platform, cwd) {
10242
10672
  },
10243
10673
  };
10244
10674
  }
10245
- function normalizeArchitectureResult({ stdout, workspaceRoot, rulesPath, techPath, researchMode, }) {
10675
+ function normalizeArchitectureResult({ stdout, workspaceRoot, researchMode, changedFiles = [], }) {
10246
10676
  const trimmed = String(stdout || "").trim();
10247
10677
  if (trimmed) {
10248
10678
  const lastLine = trimmed.split(/\r?\n/).pop();
@@ -10251,9 +10681,11 @@ function normalizeArchitectureResult({ stdout, workspaceRoot, rulesPath, techPat
10251
10681
  const parsed = JSON.parse(lastLine);
10252
10682
  return {
10253
10683
  outputRoot: workspaceRoot,
10254
- filesWritten: Array.isArray(parsed.files_written)
10255
- ? parsed.files_written
10256
- : [rulesPath, techPath],
10684
+ filesWritten: changedFiles.length > 0
10685
+ ? changedFiles
10686
+ : Array.isArray(parsed.files_written)
10687
+ ? parsed.files_written
10688
+ : [],
10257
10689
  researchUsed: typeof parsed.research_used === "boolean"
10258
10690
  ? parsed.research_used
10259
10691
  : researchMode === "always",
@@ -10271,19 +10703,42 @@ function normalizeArchitectureResult({ stdout, workspaceRoot, rulesPath, techPat
10271
10703
  }
10272
10704
  return {
10273
10705
  outputRoot: workspaceRoot,
10274
- filesWritten: [rulesPath, techPath],
10706
+ filesWritten: changedFiles,
10275
10707
  researchUsed: researchMode === "always",
10276
10708
  gaps: [],
10277
10709
  nextActions: [],
10278
10710
  rawOutput: trimmed,
10279
10711
  };
10280
10712
  }
10713
+ async function captureFileContents(filePaths) {
10714
+ const snapshot = {};
10715
+ for (const filePath of filePaths) {
10716
+ if (await pathExists(filePath)) {
10717
+ snapshot[filePath] = await readFile(filePath, "utf8");
10718
+ }
10719
+ else {
10720
+ snapshot[filePath] = null;
10721
+ }
10722
+ }
10723
+ return snapshot;
10724
+ }
10281
10725
  async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
10726
+ const specRoots = await listSpecPackRoots(workspaceRoot);
10727
+ const productPath = path.join(workspaceRoot, "PRODUCT.md");
10728
+ const architecturePath = path.join(workspaceRoot, "ARCHITECTURE.md");
10282
10729
  const rulesPath = path.join(workspaceRoot, "ENGINEERING_RULES.md");
10283
10730
  const techPath = path.join(workspaceRoot, "TECH.md");
10731
+ const roadmapPath = path.join(workspaceRoot, "ROADMAP.md");
10732
+ const adrReadmePath = path.join(workspaceRoot, "docs", "adr", "README.md");
10284
10733
  const metadataPath = path.join(workspaceRoot, ".cbx", ARCHITECTURE_BUILD_METADATA_FILENAME);
10734
+ const productExists = await pathExists(productPath);
10735
+ const architectureExists = await pathExists(architecturePath);
10285
10736
  const rulesExists = await pathExists(rulesPath);
10286
10737
  const techExists = await pathExists(techPath);
10738
+ const roadmapExists = await pathExists(roadmapPath);
10739
+ const adrReadmeExists = await pathExists(adrReadmePath);
10740
+ const expectedProductHash = hashStableObject(inferProductFoundationProfile(snapshot, specRoots));
10741
+ const expectedArchitectureHash = hashStableObject(inferArchitectureDocProfile(snapshot, specRoots));
10287
10742
  const expectedRulesHash = hashStableObject(inferArchitectureContractProfile(snapshot));
10288
10743
  const expectedTechHash = hashStableObject({
10289
10744
  style: inferArchitectureContractProfile(snapshot).style,
@@ -10291,9 +10746,43 @@ async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
10291
10746
  frameworks: snapshot.frameworks,
10292
10747
  architectureByApp: snapshot.architectureByApp,
10293
10748
  });
10749
+ const expectedRoadmapHash = hashStableObject({
10750
+ topDirs: snapshot.topDirs,
10751
+ frameworks: snapshot.frameworks,
10752
+ specRoots,
10753
+ });
10294
10754
  const findings = [];
10755
+ let actualProductHash = null;
10756
+ let actualArchitectureHash = null;
10295
10757
  let actualRulesHash = null;
10296
10758
  let actualTechHash = null;
10759
+ let actualRoadmapHash = null;
10760
+ if (!productExists) {
10761
+ findings.push("PRODUCT.md is missing.");
10762
+ }
10763
+ else {
10764
+ const content = await readFile(productPath, "utf8");
10765
+ actualProductHash = extractTaggedMarkerAttribute(content, PRODUCT_FOUNDATION_BLOCK_START_RE, "profile");
10766
+ if (!actualProductHash) {
10767
+ findings.push("PRODUCT.md is missing the managed product foundation block.");
10768
+ }
10769
+ else if (actualProductHash !== expectedProductHash) {
10770
+ findings.push(`PRODUCT.md foundation is stale (expected ${expectedProductHash}, found ${actualProductHash}).`);
10771
+ }
10772
+ }
10773
+ if (!architectureExists) {
10774
+ findings.push("ARCHITECTURE.md is missing.");
10775
+ }
10776
+ else {
10777
+ const content = await readFile(architecturePath, "utf8");
10778
+ actualArchitectureHash = extractTaggedMarkerAttribute(content, ARCHITECTURE_DOC_BLOCK_START_RE, "profile");
10779
+ if (!actualArchitectureHash) {
10780
+ findings.push("ARCHITECTURE.md is missing the managed architecture backbone block.");
10781
+ }
10782
+ else if (actualArchitectureHash !== expectedArchitectureHash) {
10783
+ findings.push(`ARCHITECTURE.md backbone is stale (expected ${expectedArchitectureHash}, found ${actualArchitectureHash}).`);
10784
+ }
10785
+ }
10297
10786
  if (!rulesExists) {
10298
10787
  findings.push("ENGINEERING_RULES.md is missing.");
10299
10788
  }
@@ -10320,6 +10809,22 @@ async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
10320
10809
  findings.push(`TECH.md architecture snapshot is stale (expected ${expectedTechHash}, found ${actualTechHash}).`);
10321
10810
  }
10322
10811
  }
10812
+ if (!roadmapExists) {
10813
+ findings.push("ROADMAP.md is missing.");
10814
+ }
10815
+ else {
10816
+ const content = await readFile(roadmapPath, "utf8");
10817
+ actualRoadmapHash = extractTaggedMarkerAttribute(content, ROADMAP_FOUNDATION_BLOCK_START_RE, "profile");
10818
+ if (!actualRoadmapHash) {
10819
+ findings.push("ROADMAP.md is missing the managed roadmap foundation block.");
10820
+ }
10821
+ else if (actualRoadmapHash !== expectedRoadmapHash) {
10822
+ findings.push(`ROADMAP.md backbone is stale (expected ${expectedRoadmapHash}, found ${actualRoadmapHash}).`);
10823
+ }
10824
+ }
10825
+ if (!adrReadmeExists) {
10826
+ findings.push("docs/adr/README.md is missing.");
10827
+ }
10323
10828
  const metadata = await readJsonFileIfExists(metadataPath);
10324
10829
  if (!metadata.exists) {
10325
10830
  findings.push("Architecture build metadata is missing.");
@@ -10327,13 +10832,23 @@ async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
10327
10832
  return {
10328
10833
  stale: findings.length > 0,
10329
10834
  findings,
10835
+ productPath,
10836
+ architecturePath,
10330
10837
  rulesPath,
10331
10838
  techPath,
10839
+ roadmapPath,
10840
+ adrReadmePath,
10332
10841
  metadataPath,
10842
+ expectedProductHash,
10843
+ expectedArchitectureHash,
10333
10844
  expectedRulesHash,
10334
10845
  expectedTechHash,
10846
+ expectedRoadmapHash,
10847
+ actualProductHash,
10848
+ actualArchitectureHash,
10335
10849
  actualRulesHash,
10336
10850
  actualTechHash,
10851
+ actualRoadmapHash,
10337
10852
  };
10338
10853
  }
10339
10854
  async function runBuildArchitecture(options) {
@@ -10347,6 +10862,7 @@ async function runBuildArchitecture(options) {
10347
10862
  const cwd = process.cwd();
10348
10863
  const workspaceRoot = findWorkspaceRoot(cwd);
10349
10864
  const snapshot = await collectTechSnapshot(workspaceRoot);
10865
+ const specRoots = await listSpecPackRoots(workspaceRoot);
10350
10866
  if (checkOnly) {
10351
10867
  const drift = await readArchitectureDriftStatus(workspaceRoot, snapshot);
10352
10868
  if (emitJson) {
@@ -10356,6 +10872,7 @@ async function runBuildArchitecture(options) {
10356
10872
  console.log(`Platform: ${platform}`);
10357
10873
  console.log(`Workspace: ${toPosixPath(workspaceRoot)}`);
10358
10874
  console.log(`Status: ${drift.stale ? "stale" : "fresh"}`);
10875
+ console.log("Backbone docs: PRODUCT.md, ARCHITECTURE.md, ENGINEERING_RULES.md, TECH.md, ROADMAP.md, docs/adr/README.md");
10359
10876
  if (drift.findings.length > 0) {
10360
10877
  console.log("Findings:");
10361
10878
  for (const finding of drift.findings) {
@@ -10367,13 +10884,25 @@ async function runBuildArchitecture(options) {
10367
10884
  process.exit(1);
10368
10885
  return;
10369
10886
  }
10887
+ const managedFilePaths = [
10888
+ path.join(workspaceRoot, "PRODUCT.md"),
10889
+ path.join(workspaceRoot, "ARCHITECTURE.md"),
10890
+ path.join(workspaceRoot, "ENGINEERING_RULES.md"),
10891
+ path.join(workspaceRoot, "TECH.md"),
10892
+ path.join(workspaceRoot, "ROADMAP.md"),
10893
+ path.join(workspaceRoot, "docs", "adr", "README.md"),
10894
+ path.join(workspaceRoot, "docs", "adr", "0000-template.md"),
10895
+ ];
10896
+ const filesBefore = dryRun
10897
+ ? Object.fromEntries(managedFilePaths.map((filePath) => [filePath, null]))
10898
+ : await captureFileContents(managedFilePaths);
10370
10899
  const scaffold = await ensureArchitectureDocScaffold({
10371
10900
  workspaceRoot,
10372
10901
  snapshot,
10902
+ specRoots,
10373
10903
  overwrite,
10374
10904
  dryRun,
10375
10905
  });
10376
- const specRoots = await listSpecPackRoots(workspaceRoot);
10377
10906
  const coreSkills = [
10378
10907
  "architecture-doc",
10379
10908
  "system-design",
@@ -10403,8 +10932,13 @@ async function runBuildArchitecture(options) {
10403
10932
  invocation: [adapter.binary, ...args],
10404
10933
  researchMode,
10405
10934
  managedTargets: [
10935
+ toPosixPath(scaffold.productPath),
10936
+ toPosixPath(scaffold.architectureDocPath),
10406
10937
  toPosixPath(scaffold.engineeringRulesPath),
10407
10938
  toPosixPath(scaffold.techMdPath),
10939
+ toPosixPath(scaffold.roadmapPath),
10940
+ toPosixPath(scaffold.adrReadmePath),
10941
+ toPosixPath(scaffold.adrTemplatePath),
10408
10942
  ],
10409
10943
  skillBundle,
10410
10944
  };
@@ -10422,30 +10956,45 @@ async function runBuildArchitecture(options) {
10422
10956
  }
10423
10957
  return;
10424
10958
  }
10425
- const execution = await execFileCapture(adapter.binary, args, {
10959
+ if (!emitJson) {
10960
+ console.log(`Streaming ${adapter.binary} output...`);
10961
+ }
10962
+ const execution = await spawnCapture(adapter.binary, args, {
10426
10963
  cwd: workspaceRoot,
10427
10964
  env: process.env,
10965
+ streamOutput: !emitJson,
10428
10966
  });
10429
10967
  if (!execution.ok) {
10430
- throw new Error(`Architecture build failed via ${adapter.binary}. ${String(execution.stderr || execution.stdout || "").trim()}`);
10431
- }
10432
- const rulesContent = await readFile(scaffold.engineeringRulesPath, "utf8");
10433
- const techContent = await readFile(scaffold.techMdPath, "utf8");
10968
+ throw new Error(explainArchitectureBuildFailure(platform, execution));
10969
+ }
10970
+ const filesAfter = await captureFileContents(managedFilePaths);
10971
+ const changedFiles = managedFilePaths
10972
+ .filter((filePath) => filesBefore[filePath] !== filesAfter[filePath])
10973
+ .map((filePath) => toPosixPath(path.relative(workspaceRoot, filePath)));
10974
+ const rulesContent = filesAfter[scaffold.engineeringRulesPath] ??
10975
+ (await readFile(scaffold.engineeringRulesPath, "utf8"));
10976
+ const techContent = filesAfter[scaffold.techMdPath] ?? (await readFile(scaffold.techMdPath, "utf8"));
10977
+ const productContent = filesAfter[scaffold.productPath] ?? (await readFile(scaffold.productPath, "utf8"));
10978
+ const architectureContent = filesAfter[scaffold.architectureDocPath] ??
10979
+ (await readFile(scaffold.architectureDocPath, "utf8"));
10980
+ const roadmapContent = filesAfter[scaffold.roadmapPath] ?? (await readFile(scaffold.roadmapPath, "utf8"));
10434
10981
  const metadataPath = path.join(workspaceRoot, ".cbx", ARCHITECTURE_BUILD_METADATA_FILENAME);
10435
10982
  const metadata = buildArchitectureBuildMetadata({
10436
10983
  platform,
10437
10984
  researchMode,
10985
+ productProfileHash: extractTaggedMarkerAttribute(productContent, PRODUCT_FOUNDATION_BLOCK_START_RE, "profile") || "unknown",
10986
+ architectureDocHash: extractTaggedMarkerAttribute(architectureContent, ARCHITECTURE_DOC_BLOCK_START_RE, "profile") || "unknown",
10438
10987
  rulesProfileHash: extractTaggedMarkerAttribute(rulesContent, ENGINEERING_ARCHITECTURE_BLOCK_START_RE, "profile") || "unknown",
10439
10988
  techSnapshotHash: extractTaggedMarkerAttribute(techContent, TECH_ARCHITECTURE_BLOCK_START_RE, "snapshot") || "unknown",
10989
+ roadmapProfileHash: extractTaggedMarkerAttribute(roadmapContent, ROADMAP_FOUNDATION_BLOCK_START_RE, "profile") || "unknown",
10440
10990
  });
10441
10991
  await mkdir(path.dirname(metadataPath), { recursive: true });
10442
10992
  await writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, "utf8");
10443
10993
  const result = normalizeArchitectureResult({
10444
10994
  stdout: execution.stdout,
10445
10995
  workspaceRoot: toPosixPath(workspaceRoot),
10446
- rulesPath: "ENGINEERING_RULES.md",
10447
- techPath: "TECH.md",
10448
10996
  researchMode,
10997
+ changedFiles: [...new Set(changedFiles)],
10449
10998
  });
10450
10999
  if (emitJson) {
10451
11000
  console.log(JSON.stringify({
@@ -10459,7 +11008,8 @@ async function runBuildArchitecture(options) {
10459
11008
  console.log(`Platform: ${platform}`);
10460
11009
  console.log(`Adapter: ${adapter.binary}`);
10461
11010
  console.log(`Workspace: ${toPosixPath(workspaceRoot)}`);
10462
- console.log(`Managed docs: ENGINEERING_RULES.md, TECH.md`);
11011
+ console.log("Managed docs: PRODUCT.md, ARCHITECTURE.md, ENGINEERING_RULES.md, TECH.md, ROADMAP.md");
11012
+ console.log("ADR scaffold: docs/adr/README.md, docs/adr/0000-template.md");
10463
11013
  console.log(`Skill bundle: ${skillBundle.join(", ")}`);
10464
11014
  console.log(`Files written: ${(result.filesWritten || []).join(", ") || "(none reported)"}`);
10465
11015
  console.log(`Research used: ${result.researchUsed ? "yes" : "no"}`);
@@ -10680,10 +11230,7 @@ async function runInitWizard(options) {
10680
11230
  dryRun: installOutcome.dryRun,
10681
11231
  });
10682
11232
  printRuleSyncResult(installOutcome.syncResult);
10683
- printInstallEngineeringSummary({
10684
- engineeringResults: installOutcome.engineeringArtifactsResult.engineeringResults,
10685
- techResult: installOutcome.engineeringArtifactsResult.techResult,
10686
- });
11233
+ printInstallDocumentationNotice();
10687
11234
  printPostmanSetupSummary({
10688
11235
  postmanSetup: installOutcome.postmanSetupResult,
10689
11236
  });