@cubis/foundry 0.3.78 → 0.3.80
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 +752 -101
- 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 +884 -114
- 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 +20 -15
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/workflows/spec.md +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/claude/workflows/architecture.md +20 -15
- package/workflows/workflows/agent-environment-setup/platforms/claude/workflows/spec.md +2 -2
- package/workflows/workflows/agent-environment-setup/platforms/codex/workflows/architecture.md +20 -15
- 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 +20 -15
- 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 +20 -15
- 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 +20 -15
- package/workflows/workflows/agent-environment-setup/shared/workflows/spec.md +2 -2
package/dist/cli/core.js
CHANGED
|
@@ -27,8 +27,16 @@ 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;
|
|
38
|
+
const FOUNDATION_DOCS_DIR = path.join("docs", "foundation");
|
|
39
|
+
const FOUNDATION_ADR_DIR = path.join(FOUNDATION_DOCS_DIR, "adr");
|
|
32
40
|
const COPILOT_ALLOWED_SKILL_FRONTMATTER_KEYS = new Set([
|
|
33
41
|
"compatibility",
|
|
34
42
|
"description",
|
|
@@ -954,6 +962,154 @@ function buildArchitectureMermaid(snapshot) {
|
|
|
954
962
|
}
|
|
955
963
|
return lines.join("\n");
|
|
956
964
|
}
|
|
965
|
+
function inferProductFoundationProfile(snapshot, specRoots = []) {
|
|
966
|
+
const appRoots = (snapshot.architectureByApp || [])
|
|
967
|
+
.map((item) => item.rootPath)
|
|
968
|
+
.filter((value) => value && value !== ".");
|
|
969
|
+
const primarySurfaces = appRoots.length > 0 ? appRoots : snapshot.topDirs.slice(0, 6);
|
|
970
|
+
const userPersonas = [];
|
|
971
|
+
if (snapshot.frameworks.includes("Flutter")) {
|
|
972
|
+
userPersonas.push("End users interacting through mobile or desktop app surfaces");
|
|
973
|
+
}
|
|
974
|
+
if (snapshot.frameworks.includes("Next.js") ||
|
|
975
|
+
snapshot.frameworks.includes("React") ||
|
|
976
|
+
snapshot.topDirs.includes("web")) {
|
|
977
|
+
userPersonas.push("Browser users and internal operators using web-facing flows");
|
|
978
|
+
}
|
|
979
|
+
if (snapshot.frameworks.includes("NestJS") || snapshot.topDirs.includes("api")) {
|
|
980
|
+
userPersonas.push("Internal services, operators, or partner systems consuming API boundaries");
|
|
981
|
+
}
|
|
982
|
+
if (userPersonas.length === 0) {
|
|
983
|
+
userPersonas.push("Project stakeholders are not obvious from the repo alone; refine personas from product context before major feature work.");
|
|
984
|
+
}
|
|
985
|
+
const coreJourneys = [];
|
|
986
|
+
if (specRoots.length > 0) {
|
|
987
|
+
coreJourneys.push(`Active implementation themes are reflected in current spec packs: ${specRoots.join(", ")}.`);
|
|
988
|
+
}
|
|
989
|
+
if (primarySurfaces.length > 0) {
|
|
990
|
+
coreJourneys.push(`Primary product surfaces currently live in: ${primarySurfaces.join(", ")}.`);
|
|
991
|
+
}
|
|
992
|
+
if (snapshot.isMcpServer || snapshot.mcpSignals.length > 0) {
|
|
993
|
+
coreJourneys.push("Tool-assisted and MCP-driven workflows are part of the operating model and should stay stable.");
|
|
994
|
+
}
|
|
995
|
+
if (coreJourneys.length === 0) {
|
|
996
|
+
coreJourneys.push("Repository evidence is thin; capture the primary user journeys here before scaling the codebase further.");
|
|
997
|
+
}
|
|
998
|
+
const successSignals = [
|
|
999
|
+
"Feature work should stay aligned to explicit user or operator value, not speculative abstractions.",
|
|
1000
|
+
"Architecture changes should reduce onboarding, debugging, and testing cost over time.",
|
|
1001
|
+
];
|
|
1002
|
+
if (snapshot.cicdSignals.length > 0) {
|
|
1003
|
+
successSignals.push(`Delivery flows already rely on ${snapshot.cicdSignals.join(", ")} signals; keep release friction low for that pipeline.`);
|
|
1004
|
+
}
|
|
1005
|
+
return {
|
|
1006
|
+
productScope: snapshot.readmeExcerpt ||
|
|
1007
|
+
"No explicit product summary was detected from repo docs. Replace this with a concise product statement when better context exists.",
|
|
1008
|
+
primarySurfaces,
|
|
1009
|
+
userPersonas,
|
|
1010
|
+
coreJourneys,
|
|
1011
|
+
successSignals,
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
function buildProductFoundationSection(snapshot, specRoots = []) {
|
|
1015
|
+
const profile = inferProductFoundationProfile(snapshot, specRoots);
|
|
1016
|
+
const hash = hashStableObject(profile);
|
|
1017
|
+
return [
|
|
1018
|
+
`<!-- cbx:product:foundation:start version=1 profile=${hash} -->`,
|
|
1019
|
+
"## Product Foundation (auto-managed)",
|
|
1020
|
+
"",
|
|
1021
|
+
"### Product Scope",
|
|
1022
|
+
profile.productScope,
|
|
1023
|
+
"",
|
|
1024
|
+
"### Primary Surfaces",
|
|
1025
|
+
...(profile.primarySurfaces.length > 0
|
|
1026
|
+
? profile.primarySurfaces.map((item) => `- ${item}`)
|
|
1027
|
+
: ["- No primary surfaces were detected automatically."]),
|
|
1028
|
+
"",
|
|
1029
|
+
"### Users and Operators",
|
|
1030
|
+
...profile.userPersonas.map((item) => `- ${item}`),
|
|
1031
|
+
"",
|
|
1032
|
+
"### Core Journeys",
|
|
1033
|
+
...profile.coreJourneys.map((item) => `- ${item}`),
|
|
1034
|
+
"",
|
|
1035
|
+
"### Success Signals",
|
|
1036
|
+
...profile.successSignals.map((item) => `- ${item}`),
|
|
1037
|
+
"",
|
|
1038
|
+
"### Product Guardrails",
|
|
1039
|
+
"- Keep product intent stable across future features so agents do not optimize for the wrong user outcome.",
|
|
1040
|
+
"- Refresh this managed section when scope, personas, operating model, or success metrics change materially.",
|
|
1041
|
+
"<!-- cbx:product:foundation:end -->",
|
|
1042
|
+
"",
|
|
1043
|
+
].join("\n");
|
|
1044
|
+
}
|
|
1045
|
+
function inferArchitectureDocProfile(snapshot, specRoots = []) {
|
|
1046
|
+
const contract = inferArchitectureContractProfile(snapshot);
|
|
1047
|
+
const architectureSignals = (snapshot.architectureByApp || []).map((item) => {
|
|
1048
|
+
const label = item.rootPath === "." ? "repo root" : item.rootPath;
|
|
1049
|
+
const signals = item.architectureSignals && item.architectureSignals.length > 0
|
|
1050
|
+
? item.architectureSignals.join(", ")
|
|
1051
|
+
: "not enough signals to classify";
|
|
1052
|
+
return `${label}: ${signals}`;
|
|
1053
|
+
});
|
|
1054
|
+
const decisionAreas = [
|
|
1055
|
+
"Module boundaries and dependency direction",
|
|
1056
|
+
"Design-system ownership and reusable primitives",
|
|
1057
|
+
"Testing and validation expectations by runtime boundary",
|
|
1058
|
+
];
|
|
1059
|
+
if (specRoots.length > 0) {
|
|
1060
|
+
decisionAreas.push(`Active specs that may influence upcoming architecture work: ${specRoots.join(", ")}.`);
|
|
1061
|
+
}
|
|
1062
|
+
return {
|
|
1063
|
+
style: contract.style,
|
|
1064
|
+
designSystemSource: contract.designSystemSource,
|
|
1065
|
+
moduleBoundaries: contract.moduleBoundaries,
|
|
1066
|
+
architectureSignals,
|
|
1067
|
+
decisionAreas,
|
|
1068
|
+
scalingConstraints: contract.scalingConstraints,
|
|
1069
|
+
testingStrategy: contract.testingStrategy,
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
function buildArchitectureDocSection(snapshot, specRoots = []) {
|
|
1073
|
+
const profile = inferArchitectureDocProfile(snapshot, specRoots);
|
|
1074
|
+
const hash = hashStableObject(profile);
|
|
1075
|
+
return [
|
|
1076
|
+
`<!-- cbx:architecture:doc:start version=1 profile=${hash} -->`,
|
|
1077
|
+
"## Accepted Architecture Backbone (auto-managed)",
|
|
1078
|
+
"",
|
|
1079
|
+
"### System Overview",
|
|
1080
|
+
`- Accepted style: ${profile.style}.`,
|
|
1081
|
+
`- Design-system source of truth: ${profile.designSystemSource}`,
|
|
1082
|
+
"",
|
|
1083
|
+
"### Bounded Contexts and Module Boundaries",
|
|
1084
|
+
...(profile.moduleBoundaries.length > 0
|
|
1085
|
+
? profile.moduleBoundaries.map((item) => `- ${item}`)
|
|
1086
|
+
: ["- No strong top-level module boundaries were detected automatically."]),
|
|
1087
|
+
"",
|
|
1088
|
+
"### Architecture Signals by Surface",
|
|
1089
|
+
...(profile.architectureSignals.length > 0
|
|
1090
|
+
? profile.architectureSignals.map((item) => `- ${item}`)
|
|
1091
|
+
: ["- No app-level architecture signals were inferred from the repo scan."]),
|
|
1092
|
+
"",
|
|
1093
|
+
"### Decision Areas to Preserve",
|
|
1094
|
+
...profile.decisionAreas.map((item) => `- ${item}`),
|
|
1095
|
+
"",
|
|
1096
|
+
"### Scalability and Reliability Notes",
|
|
1097
|
+
...profile.scalingConstraints.map((item) => `- ${item}`),
|
|
1098
|
+
"",
|
|
1099
|
+
"### Testing and Operability",
|
|
1100
|
+
...profile.testingStrategy.map((item) => `- ${item}`),
|
|
1101
|
+
"",
|
|
1102
|
+
"### ADR Linkage",
|
|
1103
|
+
"- Keep durable architecture decisions in `docs/adr/` and summarize the active decision set here.",
|
|
1104
|
+
"",
|
|
1105
|
+
"### Mermaid Diagram",
|
|
1106
|
+
"```mermaid",
|
|
1107
|
+
buildArchitectureDocMermaid(snapshot),
|
|
1108
|
+
"```",
|
|
1109
|
+
"<!-- cbx:architecture:doc:end -->",
|
|
1110
|
+
"",
|
|
1111
|
+
].join("\n");
|
|
1112
|
+
}
|
|
957
1113
|
function buildEngineeringArchitectureSection(snapshot) {
|
|
958
1114
|
const profile = inferArchitectureContractProfile(snapshot);
|
|
959
1115
|
const hash = hashStableObject(profile);
|
|
@@ -973,11 +1129,30 @@ function buildEngineeringArchitectureSection(snapshot) {
|
|
|
973
1129
|
...profile.testingStrategy.map((rule) => ` - ${rule}`),
|
|
974
1130
|
"- Doc refresh policy:",
|
|
975
1131
|
" - Update these managed sections when architecture, scale, boundaries, design-system rules, or testing strategy changes.",
|
|
976
|
-
|
|
1132
|
+
` - For non-trivial work, read ${FOUNDATION_DOCS_DIR}/PRODUCT.md, ENGINEERING_RULES.md, ${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md, and ${FOUNDATION_DOCS_DIR}/TECH.md in that order when they exist.`,
|
|
977
1133
|
"<!-- cbx:architecture:rules:end -->",
|
|
978
1134
|
"",
|
|
979
1135
|
].join("\n");
|
|
980
1136
|
}
|
|
1137
|
+
function buildArchitectureDocMermaid(snapshot) {
|
|
1138
|
+
const topDirs = snapshot.topDirs.slice(0, 5);
|
|
1139
|
+
const lines = [
|
|
1140
|
+
"flowchart LR",
|
|
1141
|
+
' product["Product Intent"] --> rules["Engineering Rules"]',
|
|
1142
|
+
' product --> arch["Architecture Backbone"]',
|
|
1143
|
+
' arch --> tech["Current Tech Snapshot"]',
|
|
1144
|
+
];
|
|
1145
|
+
if (topDirs.length > 0) {
|
|
1146
|
+
for (let index = 0; index < topDirs.length; index += 1) {
|
|
1147
|
+
const dir = topDirs[index];
|
|
1148
|
+
const nodeName = `D${index}`;
|
|
1149
|
+
lines.push(` arch --> ${nodeName}["${dir}/"]`);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
lines.push(' arch --> adr["docs/adr"]');
|
|
1153
|
+
lines.push(' product --> specs["docs/specs"]');
|
|
1154
|
+
return lines.join("\n");
|
|
1155
|
+
}
|
|
981
1156
|
function buildTechArchitectureSection(snapshot) {
|
|
982
1157
|
const profile = inferArchitectureContractProfile(snapshot);
|
|
983
1158
|
const payload = {
|
|
@@ -1030,6 +1205,48 @@ function buildTechArchitectureSection(snapshot) {
|
|
|
1030
1205
|
"",
|
|
1031
1206
|
].join("\n");
|
|
1032
1207
|
}
|
|
1208
|
+
function buildRoadmapFoundationSection(snapshot, specRoots = []) {
|
|
1209
|
+
const payload = {
|
|
1210
|
+
topDirs: snapshot.topDirs,
|
|
1211
|
+
frameworks: snapshot.frameworks,
|
|
1212
|
+
specRoots,
|
|
1213
|
+
};
|
|
1214
|
+
const hash = hashStableObject(payload);
|
|
1215
|
+
const nowItems = specRoots.length > 0
|
|
1216
|
+
? specRoots.map((item) => `Track active change planning in \`${item}\`.`)
|
|
1217
|
+
: [
|
|
1218
|
+
"No active spec packs detected. Create a spec pack before starting the next non-trivial feature or migration.",
|
|
1219
|
+
];
|
|
1220
|
+
const nextItems = [];
|
|
1221
|
+
if (snapshot.frameworks.length > 0) {
|
|
1222
|
+
nextItems.push(`Keep shared conventions stable across the current stack: ${snapshot.frameworks.join(", ")}.`);
|
|
1223
|
+
}
|
|
1224
|
+
if (snapshot.cicdSignals.length > 0) {
|
|
1225
|
+
nextItems.push(`Preserve release compatibility with the detected delivery surfaces: ${snapshot.cicdSignals.join(", ")}.`);
|
|
1226
|
+
}
|
|
1227
|
+
if (nextItems.length === 0) {
|
|
1228
|
+
nextItems.push("Document the next scaling milestones here once product direction and architecture constraints are clearer.");
|
|
1229
|
+
}
|
|
1230
|
+
return [
|
|
1231
|
+
`<!-- cbx:roadmap:foundation:start version=1 profile=${hash} -->`,
|
|
1232
|
+
"## Delivery Backbone (auto-managed)",
|
|
1233
|
+
"",
|
|
1234
|
+
"### Now",
|
|
1235
|
+
...nowItems.map((item) => `- ${item}`),
|
|
1236
|
+
"",
|
|
1237
|
+
"### Next",
|
|
1238
|
+
...nextItems.map((item) => `- ${item}`),
|
|
1239
|
+
"",
|
|
1240
|
+
"### Later",
|
|
1241
|
+
"- Use this section for medium-term scaling themes, major migrations, or cross-team architecture investments.",
|
|
1242
|
+
"",
|
|
1243
|
+
"### Backbone Maintenance",
|
|
1244
|
+
`- Keep ${FOUNDATION_DOCS_DIR}/PRODUCT.md, ${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md, ENGINEERING_RULES.md, and ${FOUNDATION_DOCS_DIR}/TECH.md aligned when direction or structure changes.`,
|
|
1245
|
+
"- Link major roadmap themes back to specs and ADRs instead of burying them in chat-only planning.",
|
|
1246
|
+
"<!-- cbx:roadmap:foundation:end -->",
|
|
1247
|
+
"",
|
|
1248
|
+
].join("\n");
|
|
1249
|
+
}
|
|
1033
1250
|
function buildEngineeringRulesTemplate() {
|
|
1034
1251
|
return [
|
|
1035
1252
|
"# Engineering Rules",
|
|
@@ -1131,17 +1348,139 @@ function buildEngineeringRulesTemplate() {
|
|
|
1131
1348
|
"",
|
|
1132
1349
|
].join("\n");
|
|
1133
1350
|
}
|
|
1134
|
-
function
|
|
1351
|
+
function buildProductTemplate(snapshot, specRoots = []) {
|
|
1352
|
+
return [
|
|
1353
|
+
"# Product",
|
|
1354
|
+
"",
|
|
1355
|
+
"This file is the durable product backbone for the project.",
|
|
1356
|
+
"",
|
|
1357
|
+
buildProductFoundationSection(snapshot, specRoots).trimEnd(),
|
|
1358
|
+
"",
|
|
1359
|
+
].join("\n");
|
|
1360
|
+
}
|
|
1361
|
+
function buildArchitectureDocTemplate(snapshot, specRoots = []) {
|
|
1362
|
+
return [
|
|
1363
|
+
"# Architecture",
|
|
1364
|
+
"",
|
|
1365
|
+
"This file captures the accepted architecture backbone for the project.",
|
|
1366
|
+
"",
|
|
1367
|
+
buildArchitectureDocSection(snapshot, specRoots).trimEnd(),
|
|
1368
|
+
"",
|
|
1369
|
+
].join("\n");
|
|
1370
|
+
}
|
|
1371
|
+
function buildRoadmapTemplate(snapshot, specRoots = []) {
|
|
1372
|
+
return [
|
|
1373
|
+
"# Roadmap",
|
|
1374
|
+
"",
|
|
1375
|
+
"This file captures the living delivery backbone for medium-term product and architecture work.",
|
|
1376
|
+
"",
|
|
1377
|
+
buildRoadmapFoundationSection(snapshot, specRoots).trimEnd(),
|
|
1378
|
+
"",
|
|
1379
|
+
].join("\n");
|
|
1380
|
+
}
|
|
1381
|
+
function buildProductBuildSkeleton() {
|
|
1382
|
+
return [
|
|
1383
|
+
"# Product",
|
|
1384
|
+
"",
|
|
1385
|
+
"This file is managed by `cbx build architecture`.",
|
|
1386
|
+
"",
|
|
1387
|
+
"<!-- cbx:product:foundation:start version=1 profile=uninitialized -->",
|
|
1388
|
+
"Replace this managed section by running `cbx build architecture --platform <codex|claude|gemini|copilot>`.",
|
|
1389
|
+
"<!-- cbx:product:foundation:end -->",
|
|
1390
|
+
"",
|
|
1391
|
+
].join("\n");
|
|
1392
|
+
}
|
|
1393
|
+
function buildArchitectureBuildSkeleton() {
|
|
1394
|
+
return [
|
|
1395
|
+
"# Architecture",
|
|
1396
|
+
"",
|
|
1397
|
+
"This file is managed by `cbx build architecture`.",
|
|
1398
|
+
"",
|
|
1399
|
+
"<!-- cbx:architecture:doc:start version=1 profile=uninitialized -->",
|
|
1400
|
+
"Replace this managed section by running `cbx build architecture --platform <codex|claude|gemini|copilot>`.",
|
|
1401
|
+
"<!-- cbx:architecture:doc:end -->",
|
|
1402
|
+
"",
|
|
1403
|
+
].join("\n");
|
|
1404
|
+
}
|
|
1405
|
+
function buildTechBuildSkeleton() {
|
|
1406
|
+
return [
|
|
1407
|
+
"# TECH.md",
|
|
1408
|
+
"",
|
|
1409
|
+
"This file is managed by `cbx build architecture`.",
|
|
1410
|
+
"",
|
|
1411
|
+
"<!-- cbx:architecture:tech:start version=1 snapshot=uninitialized -->",
|
|
1412
|
+
"Replace this managed section by running `cbx build architecture --platform <codex|claude|gemini|copilot>`.",
|
|
1413
|
+
"<!-- cbx:architecture:tech:end -->",
|
|
1414
|
+
"",
|
|
1415
|
+
].join("\n");
|
|
1416
|
+
}
|
|
1417
|
+
function buildAdrReadme() {
|
|
1418
|
+
return [
|
|
1419
|
+
"# Architecture Decision Records",
|
|
1420
|
+
"",
|
|
1421
|
+
"Use this directory for durable decisions that future contributors and agents need to preserve.",
|
|
1422
|
+
"",
|
|
1423
|
+
"## When to add an ADR",
|
|
1424
|
+
"",
|
|
1425
|
+
"- Architecture style or boundary changes",
|
|
1426
|
+
"- Data model or persistence strategy changes",
|
|
1427
|
+
"- Deployment or scaling model changes",
|
|
1428
|
+
"- Design-system ownership or shared UX pattern changes",
|
|
1429
|
+
"",
|
|
1430
|
+
"## Suggested format",
|
|
1431
|
+
"",
|
|
1432
|
+
"1. Context",
|
|
1433
|
+
"2. Decision",
|
|
1434
|
+
"3. Consequences",
|
|
1435
|
+
"4. Validation",
|
|
1436
|
+
"",
|
|
1437
|
+
"Start with `0000-template.md` and create numbered follow-up ADRs for accepted decisions.",
|
|
1438
|
+
"",
|
|
1439
|
+
].join("\n");
|
|
1440
|
+
}
|
|
1441
|
+
function buildAdrTemplate() {
|
|
1442
|
+
return [
|
|
1443
|
+
"# ADR 0000: Title",
|
|
1444
|
+
"",
|
|
1445
|
+
"## Status",
|
|
1446
|
+
"",
|
|
1447
|
+
"Proposed",
|
|
1448
|
+
"",
|
|
1449
|
+
"## Context",
|
|
1450
|
+
"",
|
|
1451
|
+
"- What problem or pressure led to this decision?",
|
|
1452
|
+
"",
|
|
1453
|
+
"## Decision",
|
|
1454
|
+
"",
|
|
1455
|
+
"- What is the chosen direction?",
|
|
1456
|
+
"",
|
|
1457
|
+
"## Consequences",
|
|
1458
|
+
"",
|
|
1459
|
+
"- What tradeoffs, benefits, or costs follow from this choice?",
|
|
1460
|
+
"",
|
|
1461
|
+
"## Validation",
|
|
1462
|
+
"",
|
|
1463
|
+
"- How will the team know this decision is working?",
|
|
1464
|
+
"",
|
|
1465
|
+
].join("\n");
|
|
1466
|
+
}
|
|
1467
|
+
function buildEngineeringRulesManagedBlock({ platform, productFilePath, architectureFilePath, engineeringRulesFilePath, techMdFilePath, roadmapFilePath, ruleFilePath, }) {
|
|
1468
|
+
const productRef = toPosixPath(path.resolve(productFilePath));
|
|
1469
|
+
const architectureRef = toPosixPath(path.resolve(architectureFilePath));
|
|
1135
1470
|
const engineeringRef = toPosixPath(path.resolve(engineeringRulesFilePath));
|
|
1136
1471
|
const techRef = toPosixPath(path.resolve(techMdFilePath));
|
|
1472
|
+
const roadmapRef = toPosixPath(path.resolve(roadmapFilePath));
|
|
1137
1473
|
const ruleRef = toPosixPath(path.resolve(ruleFilePath));
|
|
1138
1474
|
return [
|
|
1139
1475
|
`<!-- cbx:engineering:auto:start platform=${platform} version=1 -->`,
|
|
1140
1476
|
"## Engineering Guardrails (auto-managed)",
|
|
1141
1477
|
"Apply these before planning, coding, review, and release:",
|
|
1142
1478
|
"",
|
|
1479
|
+
`- Product backbone: \`${productRef}\``,
|
|
1480
|
+
`- Accepted architecture: \`${architectureRef}\``,
|
|
1143
1481
|
`- Required baseline: \`${engineeringRef}\``,
|
|
1144
1482
|
`- Project tech map: \`${techRef}\``,
|
|
1483
|
+
`- Delivery roadmap: \`${roadmapRef}\``,
|
|
1145
1484
|
`- Active platform rule file: \`${ruleRef}\``,
|
|
1146
1485
|
"",
|
|
1147
1486
|
"Hard policy:",
|
|
@@ -1149,7 +1488,7 @@ function buildEngineeringRulesManagedBlock({ platform, engineeringRulesFilePath,
|
|
|
1149
1488
|
"2. Keep architecture simple (KISS) and avoid speculative work (YAGNI).",
|
|
1150
1489
|
"3. Apply SOLID pragmatically to reduce change risk, not add ceremony.",
|
|
1151
1490
|
"4. Use clear naming with focused responsibilities and explicit boundaries.",
|
|
1152
|
-
|
|
1491
|
+
`5. For non-trivial work, read ${FOUNDATION_DOCS_DIR}/PRODUCT.md, ENGINEERING_RULES.md, ${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md, and ${FOUNDATION_DOCS_DIR}/TECH.md in that order when they exist before planning or implementation.`,
|
|
1153
1492
|
"6. Require validation evidence (lint/types/tests) before merge.",
|
|
1154
1493
|
"7. Use Decision Log response style.",
|
|
1155
1494
|
"8. Every Decision Log must include a `Skills Used` section listing skill, workflow, or agent names.",
|
|
@@ -1240,11 +1579,14 @@ async function upsertEngineeringRulesFile({ targetPath, template, overwrite = fa
|
|
|
1240
1579
|
filePath: targetPath,
|
|
1241
1580
|
};
|
|
1242
1581
|
}
|
|
1243
|
-
async function upsertEngineeringRulesBlock({ ruleFilePath, platform, engineeringRulesFilePath, techMdFilePath, dryRun = false, }) {
|
|
1582
|
+
async function upsertEngineeringRulesBlock({ ruleFilePath, platform, productFilePath, architectureFilePath, engineeringRulesFilePath, techMdFilePath, roadmapFilePath, dryRun = false, }) {
|
|
1244
1583
|
const block = buildEngineeringRulesManagedBlock({
|
|
1245
1584
|
platform,
|
|
1585
|
+
productFilePath,
|
|
1586
|
+
architectureFilePath,
|
|
1246
1587
|
engineeringRulesFilePath,
|
|
1247
1588
|
techMdFilePath,
|
|
1589
|
+
roadmapFilePath,
|
|
1248
1590
|
ruleFilePath,
|
|
1249
1591
|
});
|
|
1250
1592
|
const exists = await pathExists(ruleFilePath);
|
|
@@ -1313,6 +1655,16 @@ async function upsertTaggedSectionInFile({ targetPath, initialContent, block, st
|
|
|
1313
1655
|
nextContent =
|
|
1314
1656
|
trimmed.length > 0 ? `${trimmed}\n\n${block}\n` : `${block}\n`;
|
|
1315
1657
|
}
|
|
1658
|
+
if (!exists) {
|
|
1659
|
+
if (!dryRun) {
|
|
1660
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
1661
|
+
await writeFile(targetPath, nextContent, "utf8");
|
|
1662
|
+
}
|
|
1663
|
+
return {
|
|
1664
|
+
action: dryRun ? "would-create" : "created",
|
|
1665
|
+
filePath: targetPath,
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1316
1668
|
if (nextContent === original) {
|
|
1317
1669
|
return {
|
|
1318
1670
|
action: "unchanged",
|
|
@@ -1334,20 +1686,41 @@ async function upsertTaggedSectionInFile({ targetPath, initialContent, block, st
|
|
|
1334
1686
|
filePath: targetPath,
|
|
1335
1687
|
};
|
|
1336
1688
|
}
|
|
1337
|
-
function buildArchitectureBuildMetadata({ platform, researchMode,
|
|
1689
|
+
function buildArchitectureBuildMetadata({ platform, researchMode, managedDocs, }) {
|
|
1338
1690
|
return {
|
|
1339
|
-
schemaVersion:
|
|
1691
|
+
schemaVersion: 3,
|
|
1340
1692
|
generatedBy: "cbx build architecture",
|
|
1341
1693
|
generatedAt: new Date().toISOString(),
|
|
1342
1694
|
platform,
|
|
1343
1695
|
researchMode,
|
|
1344
|
-
|
|
1345
|
-
techSnapshotHash,
|
|
1696
|
+
managedDocs,
|
|
1346
1697
|
};
|
|
1347
1698
|
}
|
|
1348
|
-
async function ensureArchitectureDocScaffold({ workspaceRoot, snapshot, overwrite = false, dryRun = false, }) {
|
|
1699
|
+
async function ensureArchitectureDocScaffold({ workspaceRoot, snapshot, specRoots = [], overwrite = false, dryRun = false, }) {
|
|
1700
|
+
const productPath = path.join(workspaceRoot, "PRODUCT.md");
|
|
1701
|
+
const architectureDocPath = path.join(workspaceRoot, "ARCHITECTURE.md");
|
|
1349
1702
|
const engineeringRulesPath = path.join(workspaceRoot, "ENGINEERING_RULES.md");
|
|
1350
1703
|
const techMdPath = path.join(workspaceRoot, "TECH.md");
|
|
1704
|
+
const roadmapPath = path.join(workspaceRoot, "ROADMAP.md");
|
|
1705
|
+
const adrDir = path.join(workspaceRoot, "docs", "adr");
|
|
1706
|
+
const adrReadmePath = path.join(adrDir, "README.md");
|
|
1707
|
+
const adrTemplatePath = path.join(adrDir, "0000-template.md");
|
|
1708
|
+
const productResult = await upsertTaggedSectionInFile({
|
|
1709
|
+
targetPath: productPath,
|
|
1710
|
+
initialContent: `${buildProductTemplate(snapshot, specRoots)}\n`,
|
|
1711
|
+
block: buildProductFoundationSection(snapshot, specRoots),
|
|
1712
|
+
startPattern: PRODUCT_FOUNDATION_BLOCK_START_RE,
|
|
1713
|
+
endPattern: PRODUCT_FOUNDATION_BLOCK_END_RE,
|
|
1714
|
+
dryRun,
|
|
1715
|
+
});
|
|
1716
|
+
const architectureDocResult = await upsertTaggedSectionInFile({
|
|
1717
|
+
targetPath: architectureDocPath,
|
|
1718
|
+
initialContent: `${buildArchitectureDocTemplate(snapshot, specRoots)}\n`,
|
|
1719
|
+
block: buildArchitectureDocSection(snapshot, specRoots),
|
|
1720
|
+
startPattern: ARCHITECTURE_DOC_BLOCK_START_RE,
|
|
1721
|
+
endPattern: ARCHITECTURE_DOC_BLOCK_END_RE,
|
|
1722
|
+
dryRun,
|
|
1723
|
+
});
|
|
1351
1724
|
const rulesTemplate = buildEngineeringRulesTemplate();
|
|
1352
1725
|
const rulesFileResult = await upsertEngineeringRulesFile({
|
|
1353
1726
|
targetPath: engineeringRulesPath,
|
|
@@ -1378,13 +1751,115 @@ async function ensureArchitectureDocScaffold({ workspaceRoot, snapshot, overwrit
|
|
|
1378
1751
|
endPattern: TECH_ARCHITECTURE_BLOCK_END_RE,
|
|
1379
1752
|
dryRun,
|
|
1380
1753
|
});
|
|
1754
|
+
const roadmapResult = await upsertTaggedSectionInFile({
|
|
1755
|
+
targetPath: roadmapPath,
|
|
1756
|
+
initialContent: `${buildRoadmapTemplate(snapshot, specRoots)}\n`,
|
|
1757
|
+
block: buildRoadmapFoundationSection(snapshot, specRoots),
|
|
1758
|
+
startPattern: ROADMAP_FOUNDATION_BLOCK_START_RE,
|
|
1759
|
+
endPattern: ROADMAP_FOUNDATION_BLOCK_END_RE,
|
|
1760
|
+
dryRun,
|
|
1761
|
+
});
|
|
1762
|
+
const adrReadmeResult = await writeTextFile({
|
|
1763
|
+
targetPath: adrReadmePath,
|
|
1764
|
+
content: `${buildAdrReadme()}\n`,
|
|
1765
|
+
overwrite,
|
|
1766
|
+
dryRun,
|
|
1767
|
+
});
|
|
1768
|
+
const adrTemplateResult = await writeTextFile({
|
|
1769
|
+
targetPath: adrTemplatePath,
|
|
1770
|
+
content: `${buildAdrTemplate()}\n`,
|
|
1771
|
+
overwrite,
|
|
1772
|
+
dryRun,
|
|
1773
|
+
});
|
|
1381
1774
|
return {
|
|
1775
|
+
productPath,
|
|
1776
|
+
architectureDocPath,
|
|
1382
1777
|
engineeringRulesPath,
|
|
1383
1778
|
techMdPath,
|
|
1779
|
+
roadmapPath,
|
|
1780
|
+
adrReadmePath,
|
|
1781
|
+
adrTemplatePath,
|
|
1782
|
+
productResult,
|
|
1783
|
+
architectureDocResult,
|
|
1384
1784
|
rulesFileResult,
|
|
1385
1785
|
rulesArchitectureResult,
|
|
1386
1786
|
techResult,
|
|
1387
1787
|
techArchitectureResult,
|
|
1788
|
+
roadmapResult,
|
|
1789
|
+
adrReadmeResult,
|
|
1790
|
+
adrTemplateResult,
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
async function ensureArchitectureBuildScaffold({ workspaceRoot, dryRun = false, }) {
|
|
1794
|
+
const foundationRoot = path.join(workspaceRoot, FOUNDATION_DOCS_DIR);
|
|
1795
|
+
const productPath = path.join(foundationRoot, "PRODUCT.md");
|
|
1796
|
+
const architectureDocPath = path.join(foundationRoot, "ARCHITECTURE.md");
|
|
1797
|
+
const techMdPath = path.join(foundationRoot, "TECH.md");
|
|
1798
|
+
const adrDir = path.join(workspaceRoot, FOUNDATION_ADR_DIR);
|
|
1799
|
+
const adrReadmePath = path.join(adrDir, "README.md");
|
|
1800
|
+
const adrTemplatePath = path.join(adrDir, "0000-template.md");
|
|
1801
|
+
const productResult = await upsertTaggedSectionInFile({
|
|
1802
|
+
targetPath: productPath,
|
|
1803
|
+
initialContent: `${buildProductBuildSkeleton()}\n`,
|
|
1804
|
+
block: [
|
|
1805
|
+
"<!-- cbx:product:foundation:start version=1 profile=uninitialized -->",
|
|
1806
|
+
"Replace this managed section by running `cbx build architecture --platform <codex|claude|gemini|copilot>`.",
|
|
1807
|
+
"<!-- cbx:product:foundation:end -->",
|
|
1808
|
+
"",
|
|
1809
|
+
].join("\n"),
|
|
1810
|
+
startPattern: PRODUCT_FOUNDATION_BLOCK_START_RE,
|
|
1811
|
+
endPattern: PRODUCT_FOUNDATION_BLOCK_END_RE,
|
|
1812
|
+
dryRun,
|
|
1813
|
+
});
|
|
1814
|
+
const architectureDocResult = await upsertTaggedSectionInFile({
|
|
1815
|
+
targetPath: architectureDocPath,
|
|
1816
|
+
initialContent: `${buildArchitectureBuildSkeleton()}\n`,
|
|
1817
|
+
block: [
|
|
1818
|
+
"<!-- cbx:architecture:doc:start version=1 profile=uninitialized -->",
|
|
1819
|
+
"Replace this managed section by running `cbx build architecture --platform <codex|claude|gemini|copilot>`.",
|
|
1820
|
+
"<!-- cbx:architecture:doc:end -->",
|
|
1821
|
+
"",
|
|
1822
|
+
].join("\n"),
|
|
1823
|
+
startPattern: ARCHITECTURE_DOC_BLOCK_START_RE,
|
|
1824
|
+
endPattern: ARCHITECTURE_DOC_BLOCK_END_RE,
|
|
1825
|
+
dryRun,
|
|
1826
|
+
});
|
|
1827
|
+
const techResult = await upsertTaggedSectionInFile({
|
|
1828
|
+
targetPath: techMdPath,
|
|
1829
|
+
initialContent: `${buildTechBuildSkeleton()}\n`,
|
|
1830
|
+
block: [
|
|
1831
|
+
"<!-- cbx:architecture:tech:start version=1 snapshot=uninitialized -->",
|
|
1832
|
+
"Replace this managed section by running `cbx build architecture --platform <codex|claude|gemini|copilot>`.",
|
|
1833
|
+
"<!-- cbx:architecture:tech:end -->",
|
|
1834
|
+
"",
|
|
1835
|
+
].join("\n"),
|
|
1836
|
+
startPattern: TECH_ARCHITECTURE_BLOCK_START_RE,
|
|
1837
|
+
endPattern: TECH_ARCHITECTURE_BLOCK_END_RE,
|
|
1838
|
+
dryRun,
|
|
1839
|
+
});
|
|
1840
|
+
const adrReadmeResult = await writeTextFile({
|
|
1841
|
+
targetPath: adrReadmePath,
|
|
1842
|
+
content: `${buildAdrReadme()}\n`,
|
|
1843
|
+
overwrite: false,
|
|
1844
|
+
dryRun,
|
|
1845
|
+
});
|
|
1846
|
+
const adrTemplateResult = await writeTextFile({
|
|
1847
|
+
targetPath: adrTemplatePath,
|
|
1848
|
+
content: `${buildAdrTemplate()}\n`,
|
|
1849
|
+
overwrite: false,
|
|
1850
|
+
dryRun,
|
|
1851
|
+
});
|
|
1852
|
+
return {
|
|
1853
|
+
productPath,
|
|
1854
|
+
architectureDocPath,
|
|
1855
|
+
techMdPath,
|
|
1856
|
+
adrReadmePath,
|
|
1857
|
+
adrTemplatePath,
|
|
1858
|
+
productResult,
|
|
1859
|
+
architectureDocResult,
|
|
1860
|
+
techResult,
|
|
1861
|
+
adrReadmeResult,
|
|
1862
|
+
adrTemplateResult,
|
|
1388
1863
|
};
|
|
1389
1864
|
}
|
|
1390
1865
|
function normalizeTechPackageName(value) {
|
|
@@ -6958,14 +7433,6 @@ async function performWorkflowInstall(options, { postmanSelectionOverride = null
|
|
|
6958
7433
|
dryRun,
|
|
6959
7434
|
cwd,
|
|
6960
7435
|
});
|
|
6961
|
-
const engineeringArtifactsResult = await upsertEngineeringArtifacts({
|
|
6962
|
-
platform,
|
|
6963
|
-
scope: ruleScope,
|
|
6964
|
-
overwrite: false,
|
|
6965
|
-
dryRun,
|
|
6966
|
-
skipTech: false,
|
|
6967
|
-
cwd,
|
|
6968
|
-
});
|
|
6969
7436
|
const postmanSetupResult = await configurePostmanInstallArtifacts({
|
|
6970
7437
|
platform,
|
|
6971
7438
|
scope,
|
|
@@ -7007,7 +7474,7 @@ async function performWorkflowInstall(options, { postmanSelectionOverride = null
|
|
|
7007
7474
|
bundleId,
|
|
7008
7475
|
installResult,
|
|
7009
7476
|
syncResult,
|
|
7010
|
-
engineeringArtifactsResult,
|
|
7477
|
+
engineeringArtifactsResult: null,
|
|
7011
7478
|
postmanSetupResult,
|
|
7012
7479
|
terminalVerificationRuleResult,
|
|
7013
7480
|
};
|
|
@@ -7035,10 +7502,7 @@ async function runWorkflowInstall(options) {
|
|
|
7035
7502
|
dryRun: result.dryRun,
|
|
7036
7503
|
});
|
|
7037
7504
|
printRuleSyncResult(result.syncResult);
|
|
7038
|
-
|
|
7039
|
-
engineeringResults: result.engineeringArtifactsResult.engineeringResults,
|
|
7040
|
-
techResult: result.engineeringArtifactsResult.techResult,
|
|
7041
|
-
});
|
|
7505
|
+
printInstallDocumentationNotice();
|
|
7042
7506
|
printPostmanSetupSummary({
|
|
7043
7507
|
postmanSetup: result.postmanSetupResult,
|
|
7044
7508
|
});
|
|
@@ -9897,6 +10361,11 @@ function printInstallEngineeringSummary({ engineeringResults, techResult }) {
|
|
|
9897
10361
|
console.log(`- TECH scan files: ${techResult.snapshot.scannedFiles}`);
|
|
9898
10362
|
}
|
|
9899
10363
|
}
|
|
10364
|
+
function printInstallDocumentationNotice() {
|
|
10365
|
+
console.log("\nProject backbone docs:");
|
|
10366
|
+
console.log("- Install only wires the rule references and workflow assets.");
|
|
10367
|
+
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 ${FOUNDATION_DOCS_DIR}/PRODUCT.md, ${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md, ${FOUNDATION_DOCS_DIR}/TECH.md, and ADR scaffolds.`);
|
|
10368
|
+
}
|
|
9900
10369
|
async function upsertEngineeringArtifacts({ platform, scope, overwrite = false, skipTech = false, dryRun = false, cwd = process.cwd(), }) {
|
|
9901
10370
|
const ruleFilePath = await resolveRuleFilePath(platform, scope, cwd);
|
|
9902
10371
|
if (!ruleFilePath)
|
|
@@ -9906,10 +10375,10 @@ async function upsertEngineeringArtifacts({ platform, scope, overwrite = false,
|
|
|
9906
10375
|
const scaffold = await ensureArchitectureDocScaffold({
|
|
9907
10376
|
workspaceRoot,
|
|
9908
10377
|
snapshot,
|
|
10378
|
+
specRoots: [],
|
|
9909
10379
|
overwrite,
|
|
9910
10380
|
dryRun,
|
|
9911
10381
|
});
|
|
9912
|
-
const techMdPath = path.join(workspaceRoot, "TECH.md");
|
|
9913
10382
|
const targets = [{ ruleFilePath }];
|
|
9914
10383
|
if (scope === "global") {
|
|
9915
10384
|
const workspaceRuleFile = await resolveWorkspaceRuleFileForGlobalScope(platform, cwd);
|
|
@@ -9924,8 +10393,11 @@ async function upsertEngineeringArtifacts({ platform, scope, overwrite = false,
|
|
|
9924
10393
|
const blockResult = await upsertEngineeringRulesBlock({
|
|
9925
10394
|
ruleFilePath: target.ruleFilePath,
|
|
9926
10395
|
platform,
|
|
10396
|
+
productFilePath: scaffold.productPath,
|
|
10397
|
+
architectureFilePath: scaffold.architectureDocPath,
|
|
9927
10398
|
engineeringRulesFilePath: scaffold.engineeringRulesPath,
|
|
9928
|
-
techMdFilePath: techMdPath,
|
|
10399
|
+
techMdFilePath: scaffold.techMdPath,
|
|
10400
|
+
roadmapFilePath: scaffold.roadmapPath,
|
|
9929
10401
|
dryRun,
|
|
9930
10402
|
});
|
|
9931
10403
|
engineeringResults.push({
|
|
@@ -10043,6 +10515,61 @@ async function listSpecPackRoots(workspaceRoot) {
|
|
|
10043
10515
|
.sort((a, b) => a.localeCompare(b))
|
|
10044
10516
|
.slice(0, 8);
|
|
10045
10517
|
}
|
|
10518
|
+
async function resolveArchitectureInspectionAnchors(workspaceRoot, snapshot, specRoots) {
|
|
10519
|
+
const seen = new Set();
|
|
10520
|
+
const ordered = [];
|
|
10521
|
+
const pushCandidate = async (relativePath) => {
|
|
10522
|
+
const normalized = toPosixPath(relativePath);
|
|
10523
|
+
if (!normalized || seen.has(normalized))
|
|
10524
|
+
return;
|
|
10525
|
+
if (!(await pathExists(path.join(workspaceRoot, relativePath))))
|
|
10526
|
+
return;
|
|
10527
|
+
seen.add(normalized);
|
|
10528
|
+
ordered.push(normalized);
|
|
10529
|
+
};
|
|
10530
|
+
for (const candidate of [
|
|
10531
|
+
"README.md",
|
|
10532
|
+
"package.json",
|
|
10533
|
+
"pubspec.yaml",
|
|
10534
|
+
"go.mod",
|
|
10535
|
+
"pyproject.toml",
|
|
10536
|
+
"Cargo.toml",
|
|
10537
|
+
"Dockerfile",
|
|
10538
|
+
"docker-compose.yml",
|
|
10539
|
+
"docker-compose.yaml",
|
|
10540
|
+
"compose.yaml",
|
|
10541
|
+
"cbx_config.json",
|
|
10542
|
+
".vscode/mcp.json",
|
|
10543
|
+
".gemini/settings.json",
|
|
10544
|
+
]) {
|
|
10545
|
+
await pushCandidate(candidate);
|
|
10546
|
+
}
|
|
10547
|
+
for (const specRoot of specRoots.slice(0, 4)) {
|
|
10548
|
+
await pushCandidate(specRoot);
|
|
10549
|
+
}
|
|
10550
|
+
for (const app of snapshot.architectureByApp || []) {
|
|
10551
|
+
if (!app?.rootPath || app.rootPath === ".")
|
|
10552
|
+
continue;
|
|
10553
|
+
await pushCandidate(app.rootPath);
|
|
10554
|
+
for (const child of [
|
|
10555
|
+
"README.md",
|
|
10556
|
+
"src",
|
|
10557
|
+
"lib",
|
|
10558
|
+
"app",
|
|
10559
|
+
"prisma",
|
|
10560
|
+
"migrations",
|
|
10561
|
+
"test",
|
|
10562
|
+
"tests",
|
|
10563
|
+
"docs",
|
|
10564
|
+
]) {
|
|
10565
|
+
await pushCandidate(path.join(app.rootPath, child));
|
|
10566
|
+
}
|
|
10567
|
+
}
|
|
10568
|
+
for (const dir of snapshot.topDirs || []) {
|
|
10569
|
+
await pushCandidate(dir);
|
|
10570
|
+
}
|
|
10571
|
+
return ordered.slice(0, 18);
|
|
10572
|
+
}
|
|
10046
10573
|
function resolveArchitectureConditionalSkills(snapshot, specRoots, researchMode) {
|
|
10047
10574
|
const conditional = [];
|
|
10048
10575
|
const frameworks = new Set(snapshot.frameworks || []);
|
|
@@ -10087,9 +10614,12 @@ async function resolveArchitectureSkillPathHints(platform, cwd, skillIds) {
|
|
|
10087
10614
|
.map((skillId) => path.join(skillsDir, skillId, "SKILL.md"))
|
|
10088
10615
|
.map((filePath) => toPosixPath(path.relative(findWorkspaceRoot(cwd), filePath)));
|
|
10089
10616
|
}
|
|
10090
|
-
function buildArchitecturePrompt({ platform, workspaceRoot, snapshot, specRoots, researchMode, coreSkills, conditionalSkills, skillPathHints, }) {
|
|
10091
|
-
const
|
|
10092
|
-
const
|
|
10617
|
+
function buildArchitecturePrompt({ platform, workspaceRoot, snapshot, specRoots, inspectionAnchors, researchMode, coreSkills, conditionalSkills, skillPathHints, }) {
|
|
10618
|
+
const productPath = `${FOUNDATION_DOCS_DIR}/PRODUCT.md`;
|
|
10619
|
+
const architecturePath = `${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md`;
|
|
10620
|
+
const techPath = `${FOUNDATION_DOCS_DIR}/TECH.md`;
|
|
10621
|
+
const adrReadmePath = `${FOUNDATION_ADR_DIR}/README.md`;
|
|
10622
|
+
const adrTemplatePath = `${FOUNDATION_ADR_DIR}/0000-template.md`;
|
|
10093
10623
|
const architectureSignals = snapshot.architectureByApp
|
|
10094
10624
|
.filter((item) => (item.architectureSignals || []).length > 0)
|
|
10095
10625
|
.map((item) => {
|
|
@@ -10100,9 +10630,9 @@ function buildArchitecturePrompt({ platform, workspaceRoot, snapshot, specRoots,
|
|
|
10100
10630
|
`You are running inside ${platform}.`,
|
|
10101
10631
|
"",
|
|
10102
10632
|
"Objective:",
|
|
10103
|
-
`- Inspect the repository at ${toPosixPath(workspaceRoot)} and refresh the
|
|
10104
|
-
"-
|
|
10105
|
-
"- Preserve manual content outside the managed `cbx
|
|
10633
|
+
`- Inspect the repository at ${toPosixPath(workspaceRoot)} and author or refresh the core foundation docs in ${productPath}, ${architecturePath}, ${techPath}, ${adrReadmePath}, and ${adrTemplatePath}.`,
|
|
10634
|
+
"- The content should be primarily AI-authored from repository inspection, not copied from placeholder scaffolding.",
|
|
10635
|
+
"- Preserve manual content outside the managed `cbx:*` markers.",
|
|
10106
10636
|
"",
|
|
10107
10637
|
"Required skill bundle:",
|
|
10108
10638
|
`- Load these exact skill IDs first: ${coreSkills.map((skillId) => `\`${skillId}\``).join(", ")}`,
|
|
@@ -10121,24 +10651,33 @@ function buildArchitecturePrompt({ platform, workspaceRoot, snapshot, specRoots,
|
|
|
10121
10651
|
architectureSignals.length > 0
|
|
10122
10652
|
? `- Architecture signals: ${architectureSignals.join(" | ")}`
|
|
10123
10653
|
: "- Architecture signals: none confidently inferred from the repo scan",
|
|
10654
|
+
`- Entry points: ${snapshot.entryPoints.length > 0 ? snapshot.entryPoints.slice(0, 8).join(" | ") : "none detected"}`,
|
|
10655
|
+
`- Key scripts: ${snapshot.keyScripts.length > 0 ? snapshot.keyScripts.slice(0, 8).map((item) => `${item.name}=${item.command}`).join(" | ") : "none detected"}`,
|
|
10656
|
+
`- Inspection anchors: ${inspectionAnchors.length > 0 ? inspectionAnchors.join(", ") : "no concrete anchors detected; inspect the repo root, main source trees, and manifest files manually"}`,
|
|
10124
10657
|
"",
|
|
10125
10658
|
"Execution contract:",
|
|
10126
|
-
"1.
|
|
10127
|
-
"2.
|
|
10128
|
-
"3.
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10659
|
+
"1. Inspect the repository first before writing any backbone doc content. Derive structure, product surfaces, runtime boundaries, and technical constraints from the actual codebase.",
|
|
10660
|
+
"2. Complete a real inspection pass before drafting. At minimum, inspect the concrete anchors listed above, plus any adjacent directories needed to understand the main execution paths, data boundaries, and integration surfaces.",
|
|
10661
|
+
"3. Do not infer architecture from filenames alone when you can open representative files. Read enough source to validate the main app boundaries, runtime flows, and persistence/integration patterns.",
|
|
10662
|
+
`4. Then read ${productPath}, ${architecturePath}, and ${techPath} in that order when they exist so you can preserve useful manual context and update existing managed sections cleanly.`,
|
|
10663
|
+
`5. Replace or update only the content between the existing managed markers in ${productPath}, ${architecturePath}, and ${techPath}. Do not append a second marker block.`,
|
|
10664
|
+
`6. In ${productPath}, write a concrete product foundation: product purpose, primary users/operators, main journeys, business capabilities, operational constraints, and what future contributors must preserve.`,
|
|
10665
|
+
`7. In ${architecturePath}, write a lean but detailed architecture backbone in a pragmatic arc42/C4 style: system purpose and constraints, bounded contexts, major building blocks, dependency rules, data and integration boundaries, runtime flows, deployment/operability notes, testing/debugging strategy, and only the diagram levels that add real value.`,
|
|
10666
|
+
`8. In ${techPath}, write the developer-facing technical map: stack, repo layout, key commands, entrypoints, data stores, external services, environment/config surfaces, MCP/tooling footprint, and change hotspots future agents should inspect before editing code.`,
|
|
10667
|
+
"9. Every major claim should be grounded in repository evidence. Mention concrete repo paths in the docs when a structural claim would otherwise be ambiguous.",
|
|
10668
|
+
"10. Avoid placeholder filler, generic checklists, and duplicated content across files. Each doc should have a clear job.",
|
|
10669
|
+
"11. Do not create ROADMAP.md, ENGINEERING_RULES.md, or other extra docs unless the prompt explicitly asks for them.",
|
|
10132
10670
|
researchMode === "never"
|
|
10133
|
-
? "
|
|
10134
|
-
: "
|
|
10671
|
+
? "12. Stay repo-only. Do not use outside research."
|
|
10672
|
+
: "12. Use repo evidence first. Use official docs when needed. Treat Reddit or community sources only as labeled secondary evidence.",
|
|
10135
10673
|
researchMode === "always"
|
|
10136
|
-
?
|
|
10137
|
-
: "
|
|
10138
|
-
|
|
10674
|
+
? `13. Include an external research evidence subsection in ${techPath} with clearly labeled primary and secondary evidence.`
|
|
10675
|
+
: "13. Include external research notes only if they materially informed the architecture update.",
|
|
10676
|
+
`14. If the project clearly follows Clean Architecture, feature-first modules, DDD, modular monolith, or another stable structure, make that explicit in ${architecturePath} with evidence from the repo.`,
|
|
10677
|
+
`15. Ensure ${adrReadmePath} and ${adrTemplatePath} exist as ADR entrypoints, but keep them lean.`,
|
|
10139
10678
|
"",
|
|
10140
10679
|
"Return one JSON object on the last line with this shape:",
|
|
10141
|
-
|
|
10680
|
+
`{"files_written":["${productPath}","${architecturePath}","${techPath}","${adrReadmePath}","${adrTemplatePath}"],"research_used":false,"gaps":[],"next_actions":[]}`,
|
|
10142
10681
|
"",
|
|
10143
10682
|
"Do not emit placeholder TODOs in the managed sections.",
|
|
10144
10683
|
].join("\n");
|
|
@@ -10167,6 +10706,73 @@ async function execFileCapture(command, args, options = {}) {
|
|
|
10167
10706
|
};
|
|
10168
10707
|
}
|
|
10169
10708
|
}
|
|
10709
|
+
async function spawnCapture(command, args, options = {}) {
|
|
10710
|
+
const { cwd, env, streamOutput = false } = options;
|
|
10711
|
+
return await new Promise((resolve, reject) => {
|
|
10712
|
+
let stdout = "";
|
|
10713
|
+
let stderr = "";
|
|
10714
|
+
const child = spawn(command, args, {
|
|
10715
|
+
cwd,
|
|
10716
|
+
env,
|
|
10717
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
10718
|
+
});
|
|
10719
|
+
child.stdout.on("data", (chunk) => {
|
|
10720
|
+
const text = chunk.toString();
|
|
10721
|
+
stdout += text;
|
|
10722
|
+
if (streamOutput)
|
|
10723
|
+
process.stdout.write(text);
|
|
10724
|
+
});
|
|
10725
|
+
child.stderr.on("data", (chunk) => {
|
|
10726
|
+
const text = chunk.toString();
|
|
10727
|
+
stderr += text;
|
|
10728
|
+
if (streamOutput)
|
|
10729
|
+
process.stderr.write(text);
|
|
10730
|
+
});
|
|
10731
|
+
child.on("error", (error) => {
|
|
10732
|
+
if (error?.code === "ENOENT") {
|
|
10733
|
+
reject(new Error(`Required CLI '${command}' is not installed or not on PATH.`));
|
|
10734
|
+
return;
|
|
10735
|
+
}
|
|
10736
|
+
reject(error);
|
|
10737
|
+
});
|
|
10738
|
+
child.on("close", (code) => {
|
|
10739
|
+
resolve({
|
|
10740
|
+
ok: code === 0,
|
|
10741
|
+
stdout,
|
|
10742
|
+
stderr,
|
|
10743
|
+
code: code ?? 1,
|
|
10744
|
+
});
|
|
10745
|
+
});
|
|
10746
|
+
});
|
|
10747
|
+
}
|
|
10748
|
+
function explainArchitectureBuildFailure(platform, execution) {
|
|
10749
|
+
const combined = String(`${execution.stderr || ""}\n${execution.stdout || ""}`.trim());
|
|
10750
|
+
const notes = [];
|
|
10751
|
+
if (platform === "gemini") {
|
|
10752
|
+
if (combined.includes("Error during discovery for MCP server") ||
|
|
10753
|
+
combined.includes("[MCP error]")) {
|
|
10754
|
+
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.");
|
|
10755
|
+
}
|
|
10756
|
+
if (combined.includes("cloudaicompanion.companions.generateChat") ||
|
|
10757
|
+
combined.includes("PERMISSION_DENIED") ||
|
|
10758
|
+
combined.includes("403")) {
|
|
10759
|
+
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.");
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
if (platform === "claude" && combined.includes("permission")) {
|
|
10763
|
+
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.");
|
|
10764
|
+
}
|
|
10765
|
+
if (notes.length === 0) {
|
|
10766
|
+
return `Architecture build failed via ${platform}. ${combined}`.trim();
|
|
10767
|
+
}
|
|
10768
|
+
return [
|
|
10769
|
+
`Architecture build failed via ${platform}.`,
|
|
10770
|
+
...notes.map((note) => `- ${note}`),
|
|
10771
|
+
combined ? `Raw CLI output:\n${combined}` : "",
|
|
10772
|
+
]
|
|
10773
|
+
.filter(Boolean)
|
|
10774
|
+
.join("\n");
|
|
10775
|
+
}
|
|
10170
10776
|
async function probeArchitectureAdapter(platform, cwd) {
|
|
10171
10777
|
if (platform === "codex") {
|
|
10172
10778
|
const help = await execFileCapture("codex", ["exec", "--help"], { cwd });
|
|
@@ -10242,7 +10848,7 @@ async function probeArchitectureAdapter(platform, cwd) {
|
|
|
10242
10848
|
},
|
|
10243
10849
|
};
|
|
10244
10850
|
}
|
|
10245
|
-
function normalizeArchitectureResult({ stdout, workspaceRoot,
|
|
10851
|
+
function normalizeArchitectureResult({ stdout, workspaceRoot, researchMode, changedFiles = [], }) {
|
|
10246
10852
|
const trimmed = String(stdout || "").trim();
|
|
10247
10853
|
if (trimmed) {
|
|
10248
10854
|
const lastLine = trimmed.split(/\r?\n/).pop();
|
|
@@ -10251,9 +10857,11 @@ function normalizeArchitectureResult({ stdout, workspaceRoot, rulesPath, techPat
|
|
|
10251
10857
|
const parsed = JSON.parse(lastLine);
|
|
10252
10858
|
return {
|
|
10253
10859
|
outputRoot: workspaceRoot,
|
|
10254
|
-
filesWritten:
|
|
10255
|
-
?
|
|
10256
|
-
:
|
|
10860
|
+
filesWritten: changedFiles.length > 0
|
|
10861
|
+
? changedFiles
|
|
10862
|
+
: Array.isArray(parsed.files_written)
|
|
10863
|
+
? parsed.files_written
|
|
10864
|
+
: [],
|
|
10257
10865
|
researchUsed: typeof parsed.research_used === "boolean"
|
|
10258
10866
|
? parsed.research_used
|
|
10259
10867
|
: researchMode === "always",
|
|
@@ -10271,55 +10879,72 @@ function normalizeArchitectureResult({ stdout, workspaceRoot, rulesPath, techPat
|
|
|
10271
10879
|
}
|
|
10272
10880
|
return {
|
|
10273
10881
|
outputRoot: workspaceRoot,
|
|
10274
|
-
filesWritten:
|
|
10882
|
+
filesWritten: changedFiles,
|
|
10275
10883
|
researchUsed: researchMode === "always",
|
|
10276
10884
|
gaps: [],
|
|
10277
10885
|
nextActions: [],
|
|
10278
10886
|
rawOutput: trimmed,
|
|
10279
10887
|
};
|
|
10280
10888
|
}
|
|
10889
|
+
async function captureFileContents(filePaths) {
|
|
10890
|
+
const snapshot = {};
|
|
10891
|
+
for (const filePath of filePaths) {
|
|
10892
|
+
if (await pathExists(filePath)) {
|
|
10893
|
+
snapshot[filePath] = await readFile(filePath, "utf8");
|
|
10894
|
+
}
|
|
10895
|
+
else {
|
|
10896
|
+
snapshot[filePath] = null;
|
|
10897
|
+
}
|
|
10898
|
+
}
|
|
10899
|
+
return snapshot;
|
|
10900
|
+
}
|
|
10281
10901
|
async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
|
|
10282
|
-
const
|
|
10283
|
-
const
|
|
10902
|
+
const productPath = path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "PRODUCT.md");
|
|
10903
|
+
const architecturePath = path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "ARCHITECTURE.md");
|
|
10904
|
+
const techPath = path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "TECH.md");
|
|
10905
|
+
const adrReadmePath = path.join(workspaceRoot, FOUNDATION_ADR_DIR, "README.md");
|
|
10284
10906
|
const metadataPath = path.join(workspaceRoot, ".cbx", ARCHITECTURE_BUILD_METADATA_FILENAME);
|
|
10285
|
-
const
|
|
10907
|
+
const productExists = await pathExists(productPath);
|
|
10908
|
+
const architectureExists = await pathExists(architecturePath);
|
|
10286
10909
|
const techExists = await pathExists(techPath);
|
|
10287
|
-
const
|
|
10288
|
-
const expectedTechHash = hashStableObject({
|
|
10289
|
-
style: inferArchitectureContractProfile(snapshot).style,
|
|
10290
|
-
topDirs: snapshot.topDirs,
|
|
10291
|
-
frameworks: snapshot.frameworks,
|
|
10292
|
-
architectureByApp: snapshot.architectureByApp,
|
|
10293
|
-
});
|
|
10910
|
+
const adrReadmeExists = await pathExists(adrReadmePath);
|
|
10294
10911
|
const findings = [];
|
|
10295
|
-
let
|
|
10912
|
+
let actualProductHash = null;
|
|
10913
|
+
let actualArchitectureHash = null;
|
|
10296
10914
|
let actualTechHash = null;
|
|
10297
|
-
if (!
|
|
10298
|
-
findings.push(
|
|
10915
|
+
if (!productExists) {
|
|
10916
|
+
findings.push(`${FOUNDATION_DOCS_DIR}/PRODUCT.md is missing.`);
|
|
10299
10917
|
}
|
|
10300
10918
|
else {
|
|
10301
|
-
const content = await readFile(
|
|
10302
|
-
|
|
10303
|
-
if (!
|
|
10304
|
-
findings.push(
|
|
10919
|
+
const content = await readFile(productPath, "utf8");
|
|
10920
|
+
actualProductHash = extractTaggedMarkerAttribute(content, PRODUCT_FOUNDATION_BLOCK_START_RE, "profile");
|
|
10921
|
+
if (!actualProductHash) {
|
|
10922
|
+
findings.push(`${FOUNDATION_DOCS_DIR}/PRODUCT.md is missing the managed product foundation block.`);
|
|
10305
10923
|
}
|
|
10306
|
-
|
|
10307
|
-
|
|
10924
|
+
}
|
|
10925
|
+
if (!architectureExists) {
|
|
10926
|
+
findings.push(`${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md is missing.`);
|
|
10927
|
+
}
|
|
10928
|
+
else {
|
|
10929
|
+
const content = await readFile(architecturePath, "utf8");
|
|
10930
|
+
actualArchitectureHash = extractTaggedMarkerAttribute(content, ARCHITECTURE_DOC_BLOCK_START_RE, "profile");
|
|
10931
|
+
if (!actualArchitectureHash) {
|
|
10932
|
+
findings.push(`${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md is missing the managed architecture backbone block.`);
|
|
10308
10933
|
}
|
|
10309
10934
|
}
|
|
10310
10935
|
if (!techExists) {
|
|
10311
|
-
findings.push(
|
|
10936
|
+
findings.push(`${FOUNDATION_DOCS_DIR}/TECH.md is missing.`);
|
|
10312
10937
|
}
|
|
10313
10938
|
else {
|
|
10314
10939
|
const content = await readFile(techPath, "utf8");
|
|
10315
10940
|
actualTechHash = extractTaggedMarkerAttribute(content, TECH_ARCHITECTURE_BLOCK_START_RE, "snapshot");
|
|
10316
10941
|
if (!actualTechHash) {
|
|
10317
|
-
findings.push(
|
|
10318
|
-
}
|
|
10319
|
-
else if (actualTechHash !== expectedTechHash) {
|
|
10320
|
-
findings.push(`TECH.md architecture snapshot is stale (expected ${expectedTechHash}, found ${actualTechHash}).`);
|
|
10942
|
+
findings.push(`${FOUNDATION_DOCS_DIR}/TECH.md is missing the managed architecture snapshot block.`);
|
|
10321
10943
|
}
|
|
10322
10944
|
}
|
|
10945
|
+
if (!adrReadmeExists) {
|
|
10946
|
+
findings.push(`${FOUNDATION_ADR_DIR}/README.md is missing.`);
|
|
10947
|
+
}
|
|
10323
10948
|
const metadata = await readJsonFileIfExists(metadataPath);
|
|
10324
10949
|
if (!metadata.exists) {
|
|
10325
10950
|
findings.push("Architecture build metadata is missing.");
|
|
@@ -10327,12 +10952,13 @@ async function readArchitectureDriftStatus(workspaceRoot, snapshot) {
|
|
|
10327
10952
|
return {
|
|
10328
10953
|
stale: findings.length > 0,
|
|
10329
10954
|
findings,
|
|
10330
|
-
|
|
10955
|
+
productPath,
|
|
10956
|
+
architecturePath,
|
|
10331
10957
|
techPath,
|
|
10958
|
+
adrReadmePath,
|
|
10332
10959
|
metadataPath,
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
actualRulesHash,
|
|
10960
|
+
actualProductHash,
|
|
10961
|
+
actualArchitectureHash,
|
|
10336
10962
|
actualTechHash,
|
|
10337
10963
|
};
|
|
10338
10964
|
}
|
|
@@ -10347,6 +10973,7 @@ async function runBuildArchitecture(options) {
|
|
|
10347
10973
|
const cwd = process.cwd();
|
|
10348
10974
|
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
10349
10975
|
const snapshot = await collectTechSnapshot(workspaceRoot);
|
|
10976
|
+
const specRoots = await listSpecPackRoots(workspaceRoot);
|
|
10350
10977
|
if (checkOnly) {
|
|
10351
10978
|
const drift = await readArchitectureDriftStatus(workspaceRoot, snapshot);
|
|
10352
10979
|
if (emitJson) {
|
|
@@ -10356,6 +10983,7 @@ async function runBuildArchitecture(options) {
|
|
|
10356
10983
|
console.log(`Platform: ${platform}`);
|
|
10357
10984
|
console.log(`Workspace: ${toPosixPath(workspaceRoot)}`);
|
|
10358
10985
|
console.log(`Status: ${drift.stale ? "stale" : "fresh"}`);
|
|
10986
|
+
console.log(`Backbone docs: ${FOUNDATION_DOCS_DIR}/PRODUCT.md, ${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md, ${FOUNDATION_DOCS_DIR}/TECH.md, ${FOUNDATION_ADR_DIR}/README.md`);
|
|
10359
10987
|
if (drift.findings.length > 0) {
|
|
10360
10988
|
console.log("Findings:");
|
|
10361
10989
|
for (const finding of drift.findings) {
|
|
@@ -10367,13 +10995,13 @@ async function runBuildArchitecture(options) {
|
|
|
10367
10995
|
process.exit(1);
|
|
10368
10996
|
return;
|
|
10369
10997
|
}
|
|
10370
|
-
const
|
|
10371
|
-
workspaceRoot,
|
|
10372
|
-
|
|
10373
|
-
|
|
10374
|
-
|
|
10375
|
-
|
|
10376
|
-
|
|
10998
|
+
const managedFilePaths = [
|
|
10999
|
+
path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "PRODUCT.md"),
|
|
11000
|
+
path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "ARCHITECTURE.md"),
|
|
11001
|
+
path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "TECH.md"),
|
|
11002
|
+
path.join(workspaceRoot, FOUNDATION_ADR_DIR, "README.md"),
|
|
11003
|
+
path.join(workspaceRoot, FOUNDATION_ADR_DIR, "0000-template.md"),
|
|
11004
|
+
];
|
|
10377
11005
|
const coreSkills = [
|
|
10378
11006
|
"architecture-doc",
|
|
10379
11007
|
"system-design",
|
|
@@ -10383,11 +11011,13 @@ async function runBuildArchitecture(options) {
|
|
|
10383
11011
|
const conditionalSkills = resolveArchitectureConditionalSkills(snapshot, specRoots, researchMode);
|
|
10384
11012
|
const skillBundle = [...coreSkills, ...conditionalSkills];
|
|
10385
11013
|
const skillPathHints = await resolveArchitectureSkillPathHints(platform, cwd, skillBundle);
|
|
11014
|
+
const inspectionAnchors = await resolveArchitectureInspectionAnchors(workspaceRoot, snapshot, specRoots);
|
|
10386
11015
|
const prompt = buildArchitecturePrompt({
|
|
10387
11016
|
platform,
|
|
10388
11017
|
workspaceRoot,
|
|
10389
11018
|
snapshot,
|
|
10390
11019
|
specRoots,
|
|
11020
|
+
inspectionAnchors,
|
|
10391
11021
|
researchMode,
|
|
10392
11022
|
coreSkills,
|
|
10393
11023
|
conditionalSkills,
|
|
@@ -10395,6 +11025,13 @@ async function runBuildArchitecture(options) {
|
|
|
10395
11025
|
});
|
|
10396
11026
|
const adapter = await probeArchitectureAdapter(platform, workspaceRoot);
|
|
10397
11027
|
const args = adapter.buildInvocation(prompt);
|
|
11028
|
+
const managedTargets = [
|
|
11029
|
+
path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "PRODUCT.md"),
|
|
11030
|
+
path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "ARCHITECTURE.md"),
|
|
11031
|
+
path.join(workspaceRoot, FOUNDATION_DOCS_DIR, "TECH.md"),
|
|
11032
|
+
path.join(workspaceRoot, FOUNDATION_ADR_DIR, "README.md"),
|
|
11033
|
+
path.join(workspaceRoot, FOUNDATION_ADR_DIR, "0000-template.md"),
|
|
11034
|
+
].map((filePath) => toPosixPath(filePath));
|
|
10398
11035
|
if (dryRun) {
|
|
10399
11036
|
const summary = {
|
|
10400
11037
|
platform,
|
|
@@ -10402,10 +11039,7 @@ async function runBuildArchitecture(options) {
|
|
|
10402
11039
|
adapter: adapter.binary,
|
|
10403
11040
|
invocation: [adapter.binary, ...args],
|
|
10404
11041
|
researchMode,
|
|
10405
|
-
managedTargets
|
|
10406
|
-
toPosixPath(scaffold.engineeringRulesPath),
|
|
10407
|
-
toPosixPath(scaffold.techMdPath),
|
|
10408
|
-
],
|
|
11042
|
+
managedTargets,
|
|
10409
11043
|
skillBundle,
|
|
10410
11044
|
};
|
|
10411
11045
|
if (emitJson) {
|
|
@@ -10416,36 +11050,55 @@ async function runBuildArchitecture(options) {
|
|
|
10416
11050
|
console.log(`Workspace: ${toPosixPath(workspaceRoot)}`);
|
|
10417
11051
|
console.log(`Adapter: ${adapter.binary}`);
|
|
10418
11052
|
console.log(`Research mode: ${researchMode}`);
|
|
10419
|
-
console.log(`Managed targets: ${
|
|
11053
|
+
console.log(`Managed targets: ${summary.managedTargets.join(", ")}`);
|
|
10420
11054
|
console.log(`Skill bundle: ${skillBundle.join(", ")}`);
|
|
10421
11055
|
console.log(`Invocation: ${[adapter.binary, ...args].join(" ")}`);
|
|
10422
11056
|
}
|
|
10423
11057
|
return;
|
|
10424
11058
|
}
|
|
10425
|
-
const
|
|
11059
|
+
const filesBefore = await captureFileContents(managedFilePaths);
|
|
11060
|
+
const scaffold = await ensureArchitectureBuildScaffold({
|
|
11061
|
+
workspaceRoot,
|
|
11062
|
+
dryRun,
|
|
11063
|
+
});
|
|
11064
|
+
if (!emitJson) {
|
|
11065
|
+
console.log(`Streaming ${adapter.binary} output...`);
|
|
11066
|
+
}
|
|
11067
|
+
const execution = await spawnCapture(adapter.binary, args, {
|
|
10426
11068
|
cwd: workspaceRoot,
|
|
10427
11069
|
env: process.env,
|
|
11070
|
+
streamOutput: !emitJson,
|
|
10428
11071
|
});
|
|
10429
11072
|
if (!execution.ok) {
|
|
10430
|
-
throw new Error(
|
|
10431
|
-
}
|
|
10432
|
-
const
|
|
10433
|
-
const
|
|
11073
|
+
throw new Error(explainArchitectureBuildFailure(platform, execution));
|
|
11074
|
+
}
|
|
11075
|
+
const filesAfter = await captureFileContents(managedFilePaths);
|
|
11076
|
+
const changedFiles = managedFilePaths
|
|
11077
|
+
.filter((filePath) => filesBefore[filePath] !== filesAfter[filePath])
|
|
11078
|
+
.map((filePath) => toPosixPath(path.relative(workspaceRoot, filePath)));
|
|
11079
|
+
const techContent = filesAfter[scaffold.techMdPath] ?? (await readFile(scaffold.techMdPath, "utf8"));
|
|
11080
|
+
const productContent = filesAfter[scaffold.productPath] ?? (await readFile(scaffold.productPath, "utf8"));
|
|
11081
|
+
const architectureContent = filesAfter[scaffold.architectureDocPath] ??
|
|
11082
|
+
(await readFile(scaffold.architectureDocPath, "utf8"));
|
|
10434
11083
|
const metadataPath = path.join(workspaceRoot, ".cbx", ARCHITECTURE_BUILD_METADATA_FILENAME);
|
|
10435
11084
|
const metadata = buildArchitectureBuildMetadata({
|
|
10436
11085
|
platform,
|
|
10437
11086
|
researchMode,
|
|
10438
|
-
|
|
10439
|
-
|
|
11087
|
+
managedDocs: [
|
|
11088
|
+
`${FOUNDATION_DOCS_DIR}/PRODUCT.md`,
|
|
11089
|
+
`${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md`,
|
|
11090
|
+
`${FOUNDATION_DOCS_DIR}/TECH.md`,
|
|
11091
|
+
`${FOUNDATION_ADR_DIR}/README.md`,
|
|
11092
|
+
`${FOUNDATION_ADR_DIR}/0000-template.md`,
|
|
11093
|
+
],
|
|
10440
11094
|
});
|
|
10441
11095
|
await mkdir(path.dirname(metadataPath), { recursive: true });
|
|
10442
11096
|
await writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, "utf8");
|
|
10443
11097
|
const result = normalizeArchitectureResult({
|
|
10444
11098
|
stdout: execution.stdout,
|
|
10445
11099
|
workspaceRoot: toPosixPath(workspaceRoot),
|
|
10446
|
-
rulesPath: "ENGINEERING_RULES.md",
|
|
10447
|
-
techPath: "TECH.md",
|
|
10448
11100
|
researchMode,
|
|
11101
|
+
changedFiles: [...new Set(changedFiles)],
|
|
10449
11102
|
});
|
|
10450
11103
|
if (emitJson) {
|
|
10451
11104
|
console.log(JSON.stringify({
|
|
@@ -10459,7 +11112,8 @@ async function runBuildArchitecture(options) {
|
|
|
10459
11112
|
console.log(`Platform: ${platform}`);
|
|
10460
11113
|
console.log(`Adapter: ${adapter.binary}`);
|
|
10461
11114
|
console.log(`Workspace: ${toPosixPath(workspaceRoot)}`);
|
|
10462
|
-
console.log(`Managed docs:
|
|
11115
|
+
console.log(`Managed docs: ${FOUNDATION_DOCS_DIR}/PRODUCT.md, ${FOUNDATION_DOCS_DIR}/ARCHITECTURE.md, ${FOUNDATION_DOCS_DIR}/TECH.md`);
|
|
11116
|
+
console.log(`ADR scaffold: ${FOUNDATION_ADR_DIR}/README.md, ${FOUNDATION_ADR_DIR}/0000-template.md`);
|
|
10463
11117
|
console.log(`Skill bundle: ${skillBundle.join(", ")}`);
|
|
10464
11118
|
console.log(`Files written: ${(result.filesWritten || []).join(", ") || "(none reported)"}`);
|
|
10465
11119
|
console.log(`Research used: ${result.researchUsed ? "yes" : "no"}`);
|
|
@@ -10680,10 +11334,7 @@ async function runInitWizard(options) {
|
|
|
10680
11334
|
dryRun: installOutcome.dryRun,
|
|
10681
11335
|
});
|
|
10682
11336
|
printRuleSyncResult(installOutcome.syncResult);
|
|
10683
|
-
|
|
10684
|
-
engineeringResults: installOutcome.engineeringArtifactsResult.engineeringResults,
|
|
10685
|
-
techResult: installOutcome.engineeringArtifactsResult.techResult,
|
|
10686
|
-
});
|
|
11337
|
+
printInstallDocumentationNotice();
|
|
10687
11338
|
printPostmanSetupSummary({
|
|
10688
11339
|
postmanSetup: installOutcome.postmanSetupResult,
|
|
10689
11340
|
});
|