@rsaglobaltech/specgate 0.27.9 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/packages/core/src/application/ValidateChangeUseCase.js +19 -3
  3. package/dist/packages/core/src/application/ValidateChangeUseCase.js.map +1 -1
  4. package/dist/packages/core/src/domain/ApiContract.js +157 -0
  5. package/dist/packages/core/src/domain/ApiContract.js.map +1 -0
  6. package/dist/packages/core/src/domain/Approvals.js +127 -0
  7. package/dist/packages/core/src/domain/Approvals.js.map +1 -0
  8. package/dist/packages/core/src/domain/ChangeLayers.js +281 -0
  9. package/dist/packages/core/src/domain/ChangeLayers.js.map +1 -0
  10. package/dist/packages/core/src/domain/HarnessConfig.js.map +1 -1
  11. package/dist/packages/core/src/domain/HarnessGaps.js +182 -0
  12. package/dist/packages/core/src/domain/HarnessGaps.js.map +1 -0
  13. package/dist/packages/core/src/domain/HarnessReport.js +16 -1
  14. package/dist/packages/core/src/domain/HarnessReport.js.map +1 -1
  15. package/dist/packages/core/src/domain/HarnessRun.js.map +1 -1
  16. package/dist/packages/core/src/domain/Principles.js +119 -0
  17. package/dist/packages/core/src/domain/Principles.js.map +1 -0
  18. package/dist/packages/core/src/domain/RequirementGraph.js +12 -1
  19. package/dist/packages/core/src/domain/RequirementGraph.js.map +1 -1
  20. package/dist/packages/core/src/domain/Workspace.js +150 -0
  21. package/dist/packages/core/src/domain/Workspace.js.map +1 -0
  22. package/dist/packages/core/src/index.js +1 -0
  23. package/dist/packages/core/src/index.js.map +1 -1
  24. package/dist/packages/core/src/infrastructure/HarnessConfigFile.js +12 -0
  25. package/dist/packages/core/src/infrastructure/HarnessConfigFile.js.map +1 -1
  26. package/dist/packages/mcp-spec-driven/src/tools.js +18 -0
  27. package/dist/packages/mcp-spec-driven/src/tools.js.map +1 -1
  28. package/dist/scripts/alm/cli.js +45 -4
  29. package/dist/scripts/alm/cli.js.map +1 -1
  30. package/dist/scripts/alm/clients.js +3 -1
  31. package/dist/scripts/alm/clients.js.map +1 -1
  32. package/dist/scripts/alm/phases.js +279 -0
  33. package/dist/scripts/alm/phases.js.map +1 -0
  34. package/dist/scripts/alm/port.js +33 -5
  35. package/dist/scripts/alm/port.js.map +1 -1
  36. package/dist/scripts/alm/providers/azure.js +72 -11
  37. package/dist/scripts/alm/providers/azure.js.map +1 -1
  38. package/dist/scripts/alm/providers/github.js +67 -7
  39. package/dist/scripts/alm/providers/github.js.map +1 -1
  40. package/dist/scripts/alm/providers/index.js +8 -2
  41. package/dist/scripts/alm/providers/index.js.map +1 -1
  42. package/dist/scripts/alm/providers/jira.js +86 -8
  43. package/dist/scripts/alm/providers/jira.js.map +1 -1
  44. package/dist/scripts/alm/providers/linear.js +167 -0
  45. package/dist/scripts/alm/providers/linear.js.map +1 -0
  46. package/dist/scripts/change/artifacts.js +15 -1
  47. package/dist/scripts/change/artifacts.js.map +1 -1
  48. package/dist/scripts/change/instructions.js +39 -5
  49. package/dist/scripts/change/instructions.js.map +1 -1
  50. package/dist/scripts/cli/commands/alm/PhaseCommand.js +153 -0
  51. package/dist/scripts/cli/commands/alm/PhaseCommand.js.map +1 -0
  52. package/dist/scripts/cli/commands/alm/PullCommand.js +33 -2
  53. package/dist/scripts/cli/commands/alm/PullCommand.js.map +1 -1
  54. package/dist/scripts/cli/commands/alm/pull-back.js +104 -0
  55. package/dist/scripts/cli/commands/alm/pull-back.js.map +1 -0
  56. package/dist/scripts/cli/commands/change/ChangeCommand.js +92 -14
  57. package/dist/scripts/cli/commands/change/ChangeCommand.js.map +1 -1
  58. package/dist/scripts/cli/commands/change/TicketCommand.js +20 -7
  59. package/dist/scripts/cli/commands/change/TicketCommand.js.map +1 -1
  60. package/dist/scripts/cli/commands/harness/RunCommand.js +64 -20
  61. package/dist/scripts/cli/commands/harness/RunCommand.js.map +1 -1
  62. package/dist/scripts/cli/commands/project/AdoptCommand.js +44 -4
  63. package/dist/scripts/cli/commands/project/AdoptCommand.js.map +1 -1
  64. package/dist/scripts/cli/commands/project/MigrateCommand.js +166 -0
  65. package/dist/scripts/cli/commands/project/MigrateCommand.js.map +1 -0
  66. package/dist/scripts/cli/commands/project/OnboardCommand.js +23 -5
  67. package/dist/scripts/cli/commands/project/OnboardCommand.js.map +1 -1
  68. package/dist/scripts/cli/commands/quality/CheckCommand.js +25 -0
  69. package/dist/scripts/cli/commands/quality/CheckCommand.js.map +1 -1
  70. package/dist/scripts/cli/commands/quality/ValidateSpecsCommand.js +15 -0
  71. package/dist/scripts/cli/commands/quality/ValidateSpecsCommand.js.map +1 -1
  72. package/dist/scripts/cli/commands/spec/ContractCommand.js +117 -0
  73. package/dist/scripts/cli/commands/spec/ContractCommand.js.map +1 -0
  74. package/dist/scripts/cli/commands/spec/DraftCommand.js +22 -8
  75. package/dist/scripts/cli/commands/spec/DraftCommand.js.map +1 -1
  76. package/dist/scripts/cli/commands/spec/DriftCommand.js +128 -0
  77. package/dist/scripts/cli/commands/spec/DriftCommand.js.map +1 -0
  78. package/dist/scripts/cli/commands/spec/StatusCommand.js +12 -0
  79. package/dist/scripts/cli/commands/spec/StatusCommand.js.map +1 -1
  80. package/dist/scripts/cli/commands/spec/StudioCommand.js +43 -1
  81. package/dist/scripts/cli/commands/spec/StudioCommand.js.map +1 -1
  82. package/dist/scripts/cli/commands/workspace/WorkspaceCommand.js +485 -0
  83. package/dist/scripts/cli/commands/workspace/WorkspaceCommand.js.map +1 -0
  84. package/dist/scripts/contract.js +10 -0
  85. package/dist/scripts/contract.js.map +1 -0
  86. package/dist/scripts/docs/nav.js +16 -1
  87. package/dist/scripts/docs/nav.js.map +1 -1
  88. package/dist/scripts/draft/drafting-guide.js +6 -0
  89. package/dist/scripts/draft/drafting-guide.js.map +1 -1
  90. package/dist/scripts/draft/ticket-guide.js +5 -0
  91. package/dist/scripts/draft/ticket-guide.js.map +1 -1
  92. package/dist/scripts/drift.js +10 -0
  93. package/dist/scripts/drift.js.map +1 -0
  94. package/dist/scripts/harness/gap-evidence.js +149 -0
  95. package/dist/scripts/harness/gap-evidence.js.map +1 -0
  96. package/dist/scripts/harness/prompt.js +5 -1
  97. package/dist/scripts/harness/prompt.js.map +1 -1
  98. package/dist/scripts/harness/report.js +104 -5
  99. package/dist/scripts/harness/report.js.map +1 -1
  100. package/dist/scripts/lib/approvals.js +243 -0
  101. package/dist/scripts/lib/approvals.js.map +1 -0
  102. package/dist/scripts/lib/change-review.js +19 -11
  103. package/dist/scripts/lib/change-review.js.map +1 -1
  104. package/dist/scripts/lib/contracts.js +211 -0
  105. package/dist/scripts/lib/contracts.js.map +1 -0
  106. package/dist/scripts/lib/drift.js +80 -0
  107. package/dist/scripts/lib/drift.js.map +1 -0
  108. package/dist/scripts/lib/live-model.js +1 -0
  109. package/dist/scripts/lib/live-model.js.map +1 -1
  110. package/dist/scripts/lib/migrate.js +264 -0
  111. package/dist/scripts/lib/migrate.js.map +1 -0
  112. package/dist/scripts/lib/role-packages.js +230 -0
  113. package/dist/scripts/lib/role-packages.js.map +1 -0
  114. package/dist/scripts/lib/steering.js +180 -0
  115. package/dist/scripts/lib/steering.js.map +1 -0
  116. package/dist/scripts/lib/studio-app-page.js +28 -5
  117. package/dist/scripts/lib/studio-app-page.js.map +1 -1
  118. package/dist/scripts/lib/studio-app.js +29 -0
  119. package/dist/scripts/lib/studio-app.js.map +1 -1
  120. package/dist/scripts/lib/studio-route.js +20 -0
  121. package/dist/scripts/lib/studio-route.js.map +1 -1
  122. package/dist/scripts/lib/surface.js +55 -0
  123. package/dist/scripts/lib/surface.js.map +1 -1
  124. package/dist/scripts/lib/workspace-live-page.js +90 -0
  125. package/dist/scripts/lib/workspace-live-page.js.map +1 -0
  126. package/dist/scripts/lib/workspace-live.js +216 -0
  127. package/dist/scripts/lib/workspace-live.js.map +1 -0
  128. package/dist/scripts/lib/workspace.js +406 -0
  129. package/dist/scripts/lib/workspace.js.map +1 -0
  130. package/dist/scripts/migrate.js +10 -0
  131. package/dist/scripts/migrate.js.map +1 -0
  132. package/dist/scripts/schema/registry.js +12 -0
  133. package/dist/scripts/schema/registry.js.map +1 -1
  134. package/dist/scripts/workspace.js +10 -0
  135. package/dist/scripts/workspace.js.map +1 -0
  136. package/package.json +1 -1
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.partRequirements = partRequirements;
37
+ exports.readWorkspaceLive = readWorkspaceLive;
38
+ exports.workspaceLiveSignature = workspaceLiveSignature;
39
+ const fs = __importStar(require("node:fs"));
40
+ const path = __importStar(require("node:path"));
41
+ const ChangeWorkspace_1 = require("../../packages/core/src/infrastructure/ChangeWorkspace");
42
+ const SpecParser_1 = require("../../packages/core/src/domain/SpecParser");
43
+ const Workspace_1 = require("../../packages/core/src/domain/Workspace");
44
+ const live_model_1 = require("./live-model");
45
+ const in_flight_1 = require("./in-flight");
46
+ const workspace_1 = require("./workspace");
47
+ /** The requirements a sub-change's delta adds or modifies, with their dependencies and scenarios. */
48
+ function partRequirements(memberDir, change) {
49
+ const out = [];
50
+ for (const d of (0, ChangeWorkspace_1.listDeltas)(memberDir, change)) {
51
+ let parsed;
52
+ try {
53
+ parsed = (0, SpecParser_1.parseDelta)(fs.readFileSync(d.file, "utf8"));
54
+ }
55
+ catch {
56
+ continue;
57
+ }
58
+ for (const r of [...(parsed.added || []), ...(parsed.modified || [])]) {
59
+ if (!r.id)
60
+ continue;
61
+ const raw = String((r.trace || {}).depends || "");
62
+ out.push({
63
+ id: r.id.toUpperCase(),
64
+ depends: raw.split(/[,;\s]+/).filter(Boolean),
65
+ scenarios: r.scenarios.map((s) => s.id || "").filter(Boolean),
66
+ });
67
+ }
68
+ }
69
+ return out;
70
+ }
71
+ /** Minutes the member's ledger recorded, for the requirements `only` names (all when absent). */
72
+ function ledgerMinutes(memberDir, only) {
73
+ const runs = path.join(memberDir, ".harness", "runs");
74
+ let ms = 0;
75
+ let files = [];
76
+ try {
77
+ files = fs.readdirSync(runs).filter((f) => f.endsWith(".json"));
78
+ }
79
+ catch {
80
+ return 0;
81
+ }
82
+ for (const f of files) {
83
+ try {
84
+ const doc = JSON.parse(fs.readFileSync(path.join(runs, f), "utf8"));
85
+ for (const r of doc.results || []) {
86
+ if (only && !only.has(String(r.requirement)))
87
+ continue;
88
+ if (Number(r.durationMs) > 0)
89
+ ms += Number(r.durationMs);
90
+ }
91
+ }
92
+ catch {
93
+ /* a ledger being written */
94
+ }
95
+ }
96
+ return Math.round(ms / 60000);
97
+ }
98
+ function money(reqs) {
99
+ const costs = reqs
100
+ .map((r) => (r.usage && r.usage.cost !== null ? r.usage.cost : null))
101
+ .filter((c) => c !== null);
102
+ return costs.length ? Math.round(costs.reduce((a, b) => a + b, 0) * 100) / 100 : null;
103
+ }
104
+ function sumCost(costs) {
105
+ const paid = costs.map((c) => c.money).filter((m) => m !== null);
106
+ return {
107
+ money: paid.length ? Math.round(paid.reduce((a, b) => a + b, 0) * 100) / 100 : null,
108
+ minutes: costs.reduce((a, c) => a + c.minutes, 0),
109
+ };
110
+ }
111
+ function repoLive(ws, name, role, live) {
112
+ const dir = ws.dirs[name];
113
+ const reqs = live ? live.requirements : [];
114
+ const running = reqs.find((r) => r.state === "running") || null;
115
+ const lastStep = running
116
+ ? [...running.steps].reverse().find((e) => e.type === "step" && e.kind)
117
+ : undefined;
118
+ return {
119
+ name,
120
+ role,
121
+ exists: live !== null,
122
+ queue: live ? live.queue : [],
123
+ inFlight: running
124
+ ? {
125
+ req: running.req,
126
+ title: running.title,
127
+ attempt: running.attempt,
128
+ phase: running.phase,
129
+ elapsed: running.elapsed,
130
+ running: running.running,
131
+ agent: lastStep
132
+ ? { kind: String(lastStep.kind), target: String(lastStep.target || "") }
133
+ : null,
134
+ }
135
+ : null,
136
+ requirements: {
137
+ running: reqs.filter((r) => r.state === "running").length,
138
+ pass: reqs.filter((r) => r.state === "pass").length,
139
+ fail: reqs.filter((r) => r.state === "fail").length,
140
+ },
141
+ cost: { money: money(reqs), minutes: live ? ledgerMinutes(dir) : 0 },
142
+ };
143
+ }
144
+ /** The workspace's live model: every repository's harness, and every story's parts (REQ-320..322). */
145
+ function readWorkspaceLive(ws) {
146
+ const lives = new Map();
147
+ for (const r of ws.config.repos) {
148
+ const dir = ws.dirs[r.name];
149
+ lives.set(r.name, fs.existsSync(dir) ? (0, live_model_1.readLive)(dir) : null);
150
+ }
151
+ const statuses = new Map();
152
+ const statusOf = (repo, req) => {
153
+ if (!ws.dirs[repo] || !fs.existsSync(ws.dirs[repo]))
154
+ return "";
155
+ if (!statuses.has(repo))
156
+ statuses.set(repo, (0, workspace_1.memberStatuses)(ws.dirs[repo]));
157
+ return statuses.get(repo).get(req) || "";
158
+ };
159
+ const stories = (0, workspace_1.readStories)(ws.dir).map((s) => {
160
+ const costs = [];
161
+ const subchanges = s.subchanges.map((sc) => {
162
+ const dir = ws.dirs[sc.repo];
163
+ const exists = !!dir && fs.existsSync(dir);
164
+ const base = exists ? (0, workspace_1.subChangeState)(dir, sc.change) : "missing";
165
+ const reqs = exists && base === "open" ? partRequirements(dir, sc.change) : [];
166
+ const ids = new Set(reqs.map((r) => r.id));
167
+ const live = lives.get(sc.repo);
168
+ const seen = live ? live.requirements.filter((r) => ids.has(r.req)) : [];
169
+ const blockedBy = [
170
+ ...new Set(reqs.flatMap((r) => r.depends.filter((d) => {
171
+ const q = (0, Workspace_1.splitQualified)(d);
172
+ return !!q && !in_flight_1.DELIVERED.includes(statusOf(q.repo, q.id));
173
+ }))),
174
+ ];
175
+ if (exists)
176
+ costs.push({ money: money(seen), minutes: ledgerMinutes(dir, ids) });
177
+ return {
178
+ repo: sc.repo,
179
+ change: sc.change,
180
+ state: (base === "open" && seen.length > 0 ? "building" : base),
181
+ requirements: [...ids],
182
+ blockedBy,
183
+ };
184
+ });
185
+ return {
186
+ id: s.id,
187
+ title: s.title,
188
+ done: subchanges.length > 0 && subchanges.every((p) => p.state === "archived"),
189
+ subchanges,
190
+ cost: sumCost(costs),
191
+ };
192
+ });
193
+ return {
194
+ schemaVersion: 1,
195
+ name: ws.config.name,
196
+ dir: ws.dir,
197
+ repos: ws.config.repos.map((r) => repoLive(ws, r.name, r.role, lives.get(r.name) || null)),
198
+ stories,
199
+ };
200
+ }
201
+ /** Changes whenever a member's events or the workspace's stories change. */
202
+ function workspaceLiveSignature(ws) {
203
+ const parts = ws.config.repos.map((r) => fs.existsSync(ws.dirs[r.name]) ? (0, live_model_1.liveSignature)(ws.dirs[r.name]) : r.name);
204
+ const stories = path.join(ws.dir, Workspace_1.STORIES_DIR);
205
+ try {
206
+ for (const e of fs.readdirSync(stories)) {
207
+ const st = fs.statSync(path.join(stories, e, "story.yaml"));
208
+ parts.push(`${e}:${st.mtimeMs}`);
209
+ }
210
+ }
211
+ catch {
212
+ /* no stories yet */
213
+ }
214
+ return parts.join("|");
215
+ }
216
+ //# sourceMappingURL=workspace-live.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-live.js","sourceRoot":"","sources":["../../../scripts/lib/workspace-live.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,406 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LAST_CHECK_FILE = void 0;
37
+ exports.workspaceFile = workspaceFile;
38
+ exports.loadWorkspace = loadWorkspace;
39
+ exports.memberRequirements = memberRequirements;
40
+ exports.memberStatuses = memberStatuses;
41
+ exports.memberDependencies = memberDependencies;
42
+ exports.readStories = readStories;
43
+ exports.subChangeState = subChangeState;
44
+ exports.storyStates = storyStates;
45
+ exports.workspaceModel = workspaceModel;
46
+ exports.checkWorkspace = checkWorkspace;
47
+ exports.recordCheck = recordCheck;
48
+ exports.openSubChange = openSubChange;
49
+ exports.noteNoMembers = noteNoMembers;
50
+ const fs = __importStar(require("node:fs"));
51
+ const path = __importStar(require("node:path"));
52
+ const node_child_process_1 = require("node:child_process");
53
+ const Workspace_1 = require("../../packages/core/src/domain/Workspace");
54
+ const ChangeWorkspace_1 = require("../../packages/core/src/infrastructure/ChangeWorkspace");
55
+ const SpecParser_1 = require("../../packages/core/src/domain/SpecParser");
56
+ const requirement_graph_1 = require("./requirement-graph");
57
+ const in_flight_1 = require("./in-flight");
58
+ const project_root_1 = require("./project-root");
59
+ const diagnostics_1 = require("./diagnostics");
60
+ /**
61
+ * The repositories of a workspace, read from disk (ADR-0041). Every member is
62
+ * asked through the CLI itself — `status --json`, `check --json` — so the
63
+ * workspace sees exactly what each repository's own gate sees.
64
+ */
65
+ /** Where `workspace check` writes its last result, for `workspace status`. */
66
+ exports.LAST_CHECK_FILE = ".specgate/workspace-check.json";
67
+ function workspaceFile(dir) {
68
+ return path.join(dir, Workspace_1.WORKSPACE_FILE);
69
+ }
70
+ /** The workspace at `dir`, or the problems that keep it from being read. */
71
+ function loadWorkspace(dir) {
72
+ const file = workspaceFile(dir);
73
+ if (!fs.existsSync(file)) {
74
+ return {
75
+ ws: null,
76
+ problems: [
77
+ (0, diagnostics_1.error)("workspace_missing", `${dir} has no ${Workspace_1.WORKSPACE_FILE}.`, {
78
+ fix: "specgate workspace init --repo fleet=../fleet --repo web=../web:web",
79
+ }),
80
+ ],
81
+ };
82
+ }
83
+ const read = (0, Workspace_1.readWorkspace)(fs.readFileSync(file, "utf8"));
84
+ if (!read.config) {
85
+ return {
86
+ ws: null,
87
+ problems: read.problems.map((p) => (0, diagnostics_1.error)("workspace_invalid", p, { file: Workspace_1.WORKSPACE_FILE, fix: "Correct the line it names." })),
88
+ };
89
+ }
90
+ const dirs = {};
91
+ for (const r of read.config.repos)
92
+ dirs[r.name] = path.resolve(dir, r.path);
93
+ return { ws: { dir, config: read.config, dirs }, problems: [] };
94
+ }
95
+ const CLI = () => path.join((0, project_root_1.findCliRoot)(__dirname), "bin", "specgate.js");
96
+ function cli(args) {
97
+ const r = (0, node_child_process_1.spawnSync)(process.execPath, [CLI(), ...args], {
98
+ encoding: "utf8",
99
+ maxBuffer: 64 * 1024 * 1024,
100
+ env: { ...process.env, NO_COLOR: "1" },
101
+ });
102
+ return { status: r.status, stdout: r.stdout || "", stderr: r.stderr || "" };
103
+ }
104
+ function json(text) {
105
+ try {
106
+ return JSON.parse(text);
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ function branchOf(dir) {
113
+ const r = (0, node_child_process_1.spawnSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: dir, encoding: "utf8" });
114
+ return r.status === 0 ? r.stdout.trim() : null;
115
+ }
116
+ function memberRequirements(dir) {
117
+ const r = cli(["status", "--json", "--project-dir", dir]);
118
+ const doc = json(r.stdout);
119
+ if (!doc || !Array.isArray(doc.requirements))
120
+ return null;
121
+ const ids = doc.requirements.map((q) => String(q.id).toUpperCase());
122
+ const total = Number(doc.total ?? ids.length);
123
+ const pending = Number(doc.pending ?? 0);
124
+ return { total, pending, done: Math.max(0, total - pending), ids };
125
+ }
126
+ /**
127
+ * The status each requirement of a member carries: the matrix's Status column,
128
+ * then the open changes' deltas on top (what the gate judges in flight).
129
+ */
130
+ function memberStatuses(dir) {
131
+ const out = new Map();
132
+ const matrix = path.join(dir, "docs", "specs", "traceability.md");
133
+ if (fs.existsSync(matrix)) {
134
+ for (const line of fs.readFileSync(matrix, "utf8").split(/\r?\n/)) {
135
+ const cells = line.split("|").map((c) => c.trim());
136
+ if (!/^REQ-\d+$/.test(cells[1] || ""))
137
+ continue;
138
+ const status = cells[cells.length - 2] || "";
139
+ out.set(cells[1], status.replace(/`/g, ""));
140
+ }
141
+ }
142
+ for (const r of (0, in_flight_1.inFlightRequirements)(dir))
143
+ out.set(r.id.toUpperCase(), r.status);
144
+ return out;
145
+ }
146
+ /** `depends=` of a member, archived and in flight, by requirement. */
147
+ function memberDependencies(dir) {
148
+ const declared = { ...(0, requirement_graph_1.readDeclaredDependencies)(dir) };
149
+ for (const change of (0, ChangeWorkspace_1.listChangeIds)(dir)) {
150
+ for (const d of (0, ChangeWorkspace_1.listDeltas)(dir, change)) {
151
+ let parsed;
152
+ try {
153
+ parsed = (0, SpecParser_1.parseDelta)(fs.readFileSync(d.file, "utf8"));
154
+ }
155
+ catch {
156
+ continue;
157
+ }
158
+ for (const req of [...(parsed.added || []), ...(parsed.modified || [])]) {
159
+ const raw = req.id && (req.trace || {}).depends;
160
+ if (raw && req.id)
161
+ declared[req.id.toUpperCase()] = String(raw)
162
+ .split(/[,;\s]+/)
163
+ .filter(Boolean);
164
+ }
165
+ }
166
+ }
167
+ return declared;
168
+ }
169
+ /** The files a member writes its requirements in: where a qualified id may appear. */
170
+ function specFiles(dir) {
171
+ const out = [];
172
+ const add = (f) => fs.existsSync(f) && out.push(f);
173
+ add(path.join(dir, "spec.md"));
174
+ const caps = path.join(dir, "docs", "specs", "capabilities");
175
+ if (fs.existsSync(caps)) {
176
+ for (const e of fs.readdirSync(caps, { withFileTypes: true })) {
177
+ if (e.isDirectory())
178
+ add(path.join(caps, e.name, "spec.md"));
179
+ }
180
+ }
181
+ for (const change of (0, ChangeWorkspace_1.listChangeIds)(dir))
182
+ for (const d of (0, ChangeWorkspace_1.listDeltas)(dir, change))
183
+ add(d.file);
184
+ return out;
185
+ }
186
+ /** The workspace's own stories, read from `docs/stories/<id>/story.yaml`. */
187
+ function readStories(dir) {
188
+ const root = path.join(dir, Workspace_1.STORIES_DIR);
189
+ if (!fs.existsSync(root))
190
+ return [];
191
+ const out = [];
192
+ for (const e of fs.readdirSync(root, { withFileTypes: true })) {
193
+ if (!e.isDirectory())
194
+ continue;
195
+ const file = path.join(root, e.name, "story.yaml");
196
+ if (!fs.existsSync(file))
197
+ continue;
198
+ const story = (0, Workspace_1.readStory)(fs.readFileSync(file, "utf8"));
199
+ if (story)
200
+ out.push({ ...story, dir: path.join(root, e.name) });
201
+ }
202
+ return out.sort((a, b) => a.id.localeCompare(b.id));
203
+ }
204
+ /** Where a story's sub-change stands in its member: open, archived, or not there. */
205
+ function subChangeState(memberDir, change) {
206
+ if ((0, ChangeWorkspace_1.listChangeIds)(memberDir).includes(change))
207
+ return "open";
208
+ if ((0, ChangeWorkspace_1.listArchivedIds)(memberDir).some((a) => a === change || a.endsWith(`-${change}`)))
209
+ return "archived";
210
+ return "missing";
211
+ }
212
+ function lastCheck(dir) {
213
+ const file = path.join(dir, exports.LAST_CHECK_FILE);
214
+ if (!fs.existsSync(file))
215
+ return null;
216
+ return json(fs.readFileSync(file, "utf8"));
217
+ }
218
+ function storyStates(ws) {
219
+ return readStories(ws.dir).map((s) => {
220
+ const subchanges = s.subchanges.map((sc) => ({
221
+ repo: sc.repo,
222
+ change: sc.change,
223
+ state: (ws.dirs[sc.repo] && fs.existsSync(ws.dirs[sc.repo])
224
+ ? subChangeState(ws.dirs[sc.repo], sc.change)
225
+ : "missing"),
226
+ }));
227
+ return {
228
+ id: s.id,
229
+ title: s.title,
230
+ // REQ-297: done only when every repository archived its part.
231
+ done: subchanges.length > 0 && subchanges.every((sc) => sc.state === "archived"),
232
+ subchanges,
233
+ };
234
+ });
235
+ }
236
+ /** `workspace status`: each repository and each story, as one document. */
237
+ function workspaceModel(ws) {
238
+ const last = lastCheck(ws.dir);
239
+ const repos = ws.config.repos.map((r) => {
240
+ const dir = ws.dirs[r.name];
241
+ const exists = fs.existsSync(dir);
242
+ const req = exists ? memberRequirements(dir) : null;
243
+ const ran = last && r.name in last.repos;
244
+ return {
245
+ name: r.name,
246
+ role: r.role,
247
+ path: r.path,
248
+ url: r.url || null,
249
+ exists,
250
+ branch: exists ? branchOf(dir) : null,
251
+ requirements: req ? { total: req.total, done: req.done, pending: req.pending } : null,
252
+ gate: {
253
+ state: ran ? (last.repos[r.name] ? "passed" : "failed") : "not run",
254
+ at: ran ? last.at : null,
255
+ },
256
+ };
257
+ });
258
+ return {
259
+ schemaVersion: 1,
260
+ name: ws.config.name,
261
+ dir: ws.dir,
262
+ repos,
263
+ stories: storyStates(ws),
264
+ };
265
+ }
266
+ /**
267
+ * `workspace check`: each member's own gate, then what only the workspace can
268
+ * judge — qualified ids that must exist (REQ-293) and cross-repository
269
+ * dependencies that must be delivered (REQ-295).
270
+ */
271
+ function checkWorkspace(ws) {
272
+ const repos = [];
273
+ const problems = [];
274
+ const known = new Map();
275
+ const statuses = new Map();
276
+ for (const r of ws.config.repos) {
277
+ const dir = ws.dirs[r.name];
278
+ if (!fs.existsSync(dir)) {
279
+ const p = (0, diagnostics_1.error)("workspace_repo_missing", `The repo ${r.name} is not at ${r.path}.`, {
280
+ target: r.name,
281
+ fix: r.url ? `git clone ${r.url} ${r.path}` : `Correct its path in ${Workspace_1.WORKSPACE_FILE}.`,
282
+ });
283
+ repos.push({ name: r.name, ok: false, problems: [p] });
284
+ continue;
285
+ }
286
+ const run = cli(["check", dir, "--json"]);
287
+ const doc = json(run.stdout);
288
+ const own = Array.isArray(doc?.status)
289
+ ? doc.status.filter((d) => d.severity === "error")
290
+ : [];
291
+ repos.push({ name: r.name, ok: run.status === 0, problems: own });
292
+ known.set(r.name, new Set((memberRequirements(dir) || { ids: [] }).ids));
293
+ statuses.set(r.name, memberStatuses(dir));
294
+ }
295
+ // REQ-293: a qualified id names a repo of the workspace and a requirement it has.
296
+ const sources = [];
297
+ for (const s of readStories(ws.dir)) {
298
+ for (const f of fs.readdirSync(s.dir)) {
299
+ if (f.endsWith(".md"))
300
+ sources.push({ file: path.join(s.dir, f), rel: "" });
301
+ }
302
+ }
303
+ for (const r of ws.config.repos) {
304
+ if (!known.has(r.name))
305
+ continue;
306
+ for (const f of specFiles(ws.dirs[r.name]))
307
+ sources.push({ file: f, rel: r.name });
308
+ }
309
+ for (const { file, rel } of sources) {
310
+ const shown = rel
311
+ ? `${rel}/${path.relative(ws.dirs[rel], file).split(path.sep).join("/")}`
312
+ : path.relative(ws.dir, file).split(path.sep).join("/");
313
+ for (const ref of (0, Workspace_1.qualifiedRefs)(fs.readFileSync(file, "utf8"))) {
314
+ const ids = known.get(ref.repo);
315
+ if (!ws.dirs[ref.repo]) {
316
+ problems.push((0, diagnostics_1.error)("workspace_unknown_repo", `${shown}:${ref.line} names ${ref.repo}:${ref.id}, and ${ref.repo} is not a repo of this workspace.`, {
317
+ file: shown,
318
+ line: ref.line,
319
+ fix: `Add ${ref.repo} to ${Workspace_1.WORKSPACE_FILE}, or correct the repo name.`,
320
+ }));
321
+ }
322
+ else if (ids && !ids.has(ref.id)) {
323
+ problems.push((0, diagnostics_1.error)("workspace_unknown_requirement", `${shown}:${ref.line} names ${ref.repo}:${ref.id}, which ${ref.repo} does not have.`, {
324
+ file: shown,
325
+ line: ref.line,
326
+ fix: `Correct the id, or write ${ref.id} in ${ref.repo}.`,
327
+ }));
328
+ }
329
+ }
330
+ }
331
+ // REQ-295: a requirement that depends on another repo's waits for it.
332
+ for (const r of ws.config.repos) {
333
+ if (!known.has(r.name))
334
+ continue;
335
+ for (const [req, deps] of Object.entries(memberDependencies(ws.dirs[r.name]))) {
336
+ for (const dep of deps) {
337
+ const q = (0, Workspace_1.splitQualified)(dep);
338
+ if (!q)
339
+ continue;
340
+ const theirs = statuses.get(q.repo);
341
+ if (!theirs) {
342
+ if (!ws.dirs[q.repo]) {
343
+ problems.push((0, diagnostics_1.error)("workspace_unknown_repo", `${r.name}:${req} depends on ${q.repo}:${q.id}, and ${q.repo} is not a repo of this workspace.`, {
344
+ target: `${r.name}:${req}`,
345
+ fix: `Add ${q.repo} to ${Workspace_1.WORKSPACE_FILE}, or correct depends= in ${req}.`,
346
+ }));
347
+ }
348
+ continue;
349
+ }
350
+ const status = theirs.get(q.id);
351
+ if (status === undefined) {
352
+ problems.push((0, diagnostics_1.error)("workspace_unknown_requirement", `${r.name}:${req} depends on ${q.repo}:${q.id}, which ${q.repo} does not have.`, {
353
+ target: `${r.name}:${req}`,
354
+ fix: `Correct depends= in ${req}, or write ${q.id} in ${q.repo}.`,
355
+ }));
356
+ }
357
+ else if (!in_flight_1.DELIVERED.includes(status)) {
358
+ problems.push((0, diagnostics_1.error)("workspace_dependency_pending", `${r.name}:${req} depends on ${q.repo}:${q.id}, which is ${status}, not delivered.`, {
359
+ target: `${r.name}:${req}`,
360
+ fix: `Deliver ${q.id} in ${q.repo} first (specgate harness run --req ${q.id} there).`,
361
+ }));
362
+ }
363
+ }
364
+ }
365
+ }
366
+ const ok = repos.every((r) => r.ok) && problems.length === 0;
367
+ return { ok, repos, problems };
368
+ }
369
+ function recordCheck(dir, result) {
370
+ const file = path.join(dir, exports.LAST_CHECK_FILE);
371
+ fs.mkdirSync(path.dirname(file), { recursive: true });
372
+ const repos = {};
373
+ for (const r of result.repos)
374
+ repos[r.name] = r.ok;
375
+ fs.writeFileSync(file, JSON.stringify({ at: new Date().toISOString(), repos }, null, 2) + "\n");
376
+ }
377
+ // ── workspace propose ───────────────────────────────────────────────────────
378
+ /** Open the story's change in one member, linked back to the story. */
379
+ function openSubChange(ws, repo, story, title) {
380
+ const dir = ws.dirs[repo];
381
+ const r = cli(["change", "new", story, "--project-dir", dir]);
382
+ if (r.status !== 0) {
383
+ return (0, diagnostics_1.error)("workspace_propose_failed", `change new ${story} failed in ${repo}: ${(r.stderr || r.stdout).trim().split("\n").pop()}`, {
384
+ target: repo,
385
+ fix: `Run specgate change new ${story} in ${repo} to see why.`,
386
+ });
387
+ }
388
+ // A 1.0 change's Discover (ADR-0042), or a 0.27 change's proposal.
389
+ const changeDir = path.join(dir, "docs", "specs", "changes", story);
390
+ const proposal = ["discover.md", "proposal.md"]
391
+ .map((f) => path.join(changeDir, f))
392
+ .find((f) => fs.existsSync(f));
393
+ if (proposal) {
394
+ const text = fs.readFileSync(proposal, "utf8");
395
+ const link = `\n> Part of the workspace story \`${ws.config.name}:${story}\` — ${title}. ` +
396
+ `The story and its other repositories: \`${Workspace_1.STORIES_DIR}/${story}/story.md\` in the workspace repository.\n`;
397
+ fs.writeFileSync(proposal, text.replace(/^(# .*\n)/, `$1${link}`), "utf8");
398
+ }
399
+ return null;
400
+ }
401
+ function noteNoMembers() {
402
+ return (0, diagnostics_1.warning)("workspace_no_members", "No sibling folder holds a Specgate project.", {
403
+ fix: "specgate workspace init --repo fleet=../fleet --repo web=../web:web",
404
+ });
405
+ }
406
+ //# sourceMappingURL=workspace.js.map