@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.
- package/dist/cli/build/commands.js +1 -1
- package/dist/cli/build/commands.js.map +1 -1
- package/dist/cli/core.js +601 -54
- package/dist/cli/core.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/build/commands.ts +1 -1
- package/src/cli/core.ts +736 -54
- package/workflows/workflows/agent-environment-setup/generated/route-manifest.json +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/accessibility.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/architecture.toml +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/backend.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/create.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/database.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/debug.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/devops.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/implement-track.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/migrate.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/mobile.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/onboard.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/orchestrate.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/plan.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/refactor.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/release.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/review.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/security.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/spec.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/test.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/commands/vercel.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/workflows/architecture.md +17 -12
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/workflows/spec.md +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/claude/workflows/architecture.md +17 -12
- package/workflows/workflows/agent-environment-setup/platforms/claude/workflows/spec.md +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/codex/workflows/architecture.md +17 -12
- package/workflows/workflows/agent-environment-setup/platforms/codex/workflows/spec.md +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-accessibility.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-architecture.prompt.md +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-backend.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-create.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-database.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-debug.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-devops.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-implement-track.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-migrate.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-mobile.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-onboard.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-orchestrate.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-plan.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-refactor.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-release.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-review.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-security.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-spec.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-test.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/prompts/workflow-vercel.prompt.md +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/copilot/workflows/architecture.md +17 -12
- package/workflows/workflows/agent-environment-setup/platforms/copilot/workflows/spec.md +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/accessibility.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/architecture.toml +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/backend.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/create.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/database.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/debug.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/devops.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/implement-track.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/migrate.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/mobile.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/onboard.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/orchestrate.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/plan.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/refactor.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/release.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/review.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/security.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/spec.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/test.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/commands/vercel.toml +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/gemini/workflows/architecture.md +17 -12
- package/workflows/workflows/agent-environment-setup/platforms/gemini/workflows/spec.md +2 -2
- package/workflows/workflows/agent-environment-setup/shared/rules/STEERING.md +3 -3
- package/workflows/workflows/agent-environment-setup/shared/workflows/architecture.md +17 -12
- 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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
12558
|
-
"- Keep ENGINEERING_RULES.md normative
|
|
12559
|
-
"- Preserve manual content outside the managed `cbx
|
|
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
|
-
|
|
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
|
|
12583
|
-
"4.
|
|
12584
|
-
"5. In
|
|
12585
|
-
"6. In
|
|
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
|
-
? "
|
|
12588
|
-
: "
|
|
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
|
-
? "
|
|
12591
|
-
: "
|
|
12592
|
-
"
|
|
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:
|
|
12726
|
-
|
|
12727
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
13277
|
-
engineeringResults:
|
|
13278
|
-
installOutcome.engineeringArtifactsResult.engineeringResults,
|
|
13279
|
-
techResult: installOutcome.engineeringArtifactsResult.techResult,
|
|
13280
|
-
});
|
|
13962
|
+
printInstallDocumentationNotice();
|
|
13281
13963
|
printPostmanSetupSummary({
|
|
13282
13964
|
postmanSetup: installOutcome.postmanSetupResult,
|
|
13283
13965
|
});
|