@popoverai/dotrequirements 0.33.0 → 0.34.0

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 (115) hide show
  1. package/dist/cli.js +13 -1
  2. package/dist/cloud.d.ts +5 -1
  3. package/dist/cloud.js +6 -2
  4. package/dist/commands/board/index.d.ts +13 -0
  5. package/dist/commands/board/index.js +96 -0
  6. package/dist/commands/branch-merge.d.ts +78 -0
  7. package/dist/commands/branch-merge.js +129 -0
  8. package/dist/commands/diff.js +15 -5
  9. package/dist/commands/mockup/index.d.ts +11 -0
  10. package/dist/commands/mockup/index.js +82 -0
  11. package/dist/commands/scores/index.d.ts +50 -0
  12. package/dist/commands/scores/index.js +190 -0
  13. package/dist/commands/solution/ask.d.ts +9 -0
  14. package/dist/commands/solution/ask.js +27 -0
  15. package/dist/commands/solution/author.d.ts +19 -0
  16. package/dist/commands/solution/author.js +202 -0
  17. package/dist/commands/solution/branch.d.ts +103 -0
  18. package/dist/commands/solution/branch.js +341 -0
  19. package/dist/commands/solution/brief.d.ts +81 -0
  20. package/dist/commands/solution/brief.js +47 -0
  21. package/dist/commands/solution/checkout.d.ts +27 -0
  22. package/dist/commands/solution/checkout.js +66 -0
  23. package/dist/commands/solution/client.d.ts +16 -0
  24. package/dist/commands/solution/client.js +46 -0
  25. package/dist/commands/solution/contract.d.ts +9 -0
  26. package/dist/commands/solution/contract.js +23 -0
  27. package/dist/commands/solution/create.d.ts +14 -0
  28. package/dist/commands/solution/create.js +52 -0
  29. package/dist/commands/solution/demo-script.d.ts +28 -0
  30. package/dist/commands/solution/demo-script.js +43 -0
  31. package/dist/commands/solution/image.d.ts +60 -0
  32. package/dist/commands/solution/image.js +148 -0
  33. package/dist/commands/solution/index.d.ts +12 -0
  34. package/dist/commands/solution/index.js +281 -0
  35. package/dist/commands/solution/look-up.d.ts +9 -0
  36. package/dist/commands/solution/look-up.js +69 -0
  37. package/dist/commands/solution/next.d.ts +12 -0
  38. package/dist/commands/solution/next.js +48 -0
  39. package/dist/commands/solution/plan-file.d.ts +22 -0
  40. package/dist/commands/solution/plan-file.js +33 -0
  41. package/dist/commands/solution/preview-screenshot.d.ts +21 -0
  42. package/dist/commands/solution/preview-screenshot.js +56 -0
  43. package/dist/commands/solution/pull-request.d.ts +25 -0
  44. package/dist/commands/solution/pull-request.js +117 -0
  45. package/dist/commands/solution/pushing.d.ts +66 -0
  46. package/dist/commands/solution/pushing.js +185 -0
  47. package/dist/commands/solution/rehearse.d.ts +10 -0
  48. package/dist/commands/solution/rehearse.js +24 -0
  49. package/dist/commands/solution/request.d.ts +3 -0
  50. package/dist/commands/solution/request.js +13 -0
  51. package/dist/commands/solution/review.d.ts +27 -0
  52. package/dist/commands/solution/review.js +104 -0
  53. package/dist/commands/solution/run-file.d.ts +42 -0
  54. package/dist/commands/solution/run-file.js +63 -0
  55. package/dist/commands/solution/share.d.ts +62 -0
  56. package/dist/commands/solution/share.js +346 -0
  57. package/dist/commands/solution/stall.d.ts +14 -0
  58. package/dist/commands/solution/stall.js +41 -0
  59. package/dist/commands/solution/start.d.ts +11 -0
  60. package/dist/commands/solution/start.js +20 -0
  61. package/dist/commands/solution/status.d.ts +39 -0
  62. package/dist/commands/solution/status.js +145 -0
  63. package/dist/commands/solution/summary.d.ts +13 -0
  64. package/dist/commands/solution/summary.js +5 -0
  65. package/dist/commands/solution/things.d.ts +118 -0
  66. package/dist/commands/solution/things.js +78 -0
  67. package/dist/commands/solution/thread.d.ts +13 -0
  68. package/dist/commands/solution/thread.js +53 -0
  69. package/dist/commands/solution/upload.d.ts +24 -0
  70. package/dist/commands/solution/upload.js +27 -0
  71. package/dist/commands/solution/video.d.ts +88 -0
  72. package/dist/commands/solution/video.js +364 -0
  73. package/dist/commands/solution/working-folder.d.ts +44 -0
  74. package/dist/commands/solution/working-folder.js +64 -0
  75. package/dist/commands/sync.d.ts +2 -1
  76. package/dist/commands/sync.js +21 -5
  77. package/dist/convex.d.ts +54 -0
  78. package/dist/convex.js +64 -0
  79. package/dist/push/core.js +7 -2
  80. package/dist/rehearsal/derive.d.ts +23 -0
  81. package/dist/rehearsal/derive.js +112 -0
  82. package/dist/rehearsal/driver.d.ts +44 -0
  83. package/dist/rehearsal/driver.js +337 -0
  84. package/dist/rehearsal/earlier.d.ts +25 -0
  85. package/dist/rehearsal/earlier.js +138 -0
  86. package/dist/rehearsal/index.d.ts +5 -0
  87. package/dist/rehearsal/index.js +4 -0
  88. package/dist/rehearsal/live.d.ts +72 -0
  89. package/dist/rehearsal/live.js +301 -0
  90. package/dist/rehearsal/report.d.ts +4 -0
  91. package/dist/rehearsal/report.js +69 -0
  92. package/dist/rehearsal/types.d.ts +377 -0
  93. package/dist/rehearsal/types.js +8 -0
  94. package/dist/requirements/index.d.ts +6 -0
  95. package/dist/requirements/index.js +24 -0
  96. package/dist/schema/browser.d.ts +1 -1
  97. package/dist/schema/browser.js +3 -1
  98. package/dist/schema/file-writer.d.ts +5 -0
  99. package/dist/schema/file-writer.js +4 -0
  100. package/dist/schema/parser.d.ts +11 -2
  101. package/dist/schema/parser.js +11 -7
  102. package/dist/schema/schemas.d.ts +40 -0
  103. package/dist/schema/schemas.js +4 -0
  104. package/dist/sync/execute.d.ts +10 -1
  105. package/dist/sync/execute.js +69 -5
  106. package/dist/sync/index.d.ts +1 -1
  107. package/dist/sync/index.js +1 -1
  108. package/dist/sync/publish.d.ts +7 -0
  109. package/dist/sync/segment.js +4 -1
  110. package/dist/sync/snapshot.d.ts +23 -1
  111. package/dist/sync/snapshot.js +75 -3
  112. package/dist/sync/solution-documents.d.ts +64 -0
  113. package/dist/sync/solution-documents.js +97 -0
  114. package/dist/sync/types.d.ts +18 -0
  115. package/package.json +3 -2
@@ -0,0 +1,377 @@
1
+ /**
2
+ * A rehearsal (docs/working/rehearsing-steps.md): a plan of outcomes, the
3
+ * artifacts its inputs hold, and who plays each agent the backend sends
4
+ * for — a real one, or a script that plays it from the brief the agent
5
+ * would get.
6
+ */
7
+ export type Thing = {
8
+ kind: "document";
9
+ documentId: string;
10
+ } | {
11
+ kind: "board";
12
+ boardId: string;
13
+ } | {
14
+ kind: "mockup";
15
+ mockupId: string;
16
+ } | {
17
+ kind: "file";
18
+ path: string;
19
+ branch?: string;
20
+ } | {
21
+ kind: "link";
22
+ url: string;
23
+ title?: string;
24
+ }
25
+ /** Something running in the sandbox, with the title a script names it by, if any (SIGHT-12.3). */
26
+ | {
27
+ kind: "preview";
28
+ port: number;
29
+ page?: string;
30
+ command: string;
31
+ title?: string;
32
+ }
33
+ /** The solution's pull request: a script submits it as given, checked against no repository (PULL-3.3). */
34
+ | {
35
+ kind: "pull request";
36
+ url: string;
37
+ number: number;
38
+ }
39
+ /** An image kept under its file name (SIGHT-9), as a scenario gives one to an input (REHEARSE-4.7). */
40
+ | {
41
+ kind: "image";
42
+ storageId: string;
43
+ name: string;
44
+ } | ({
45
+ kind: "scores";
46
+ } & Scores);
47
+ /** One axis of a solution's scores: a whole number from 1 to 5, and why. */
48
+ export type AxisScore = {
49
+ score: number;
50
+ reason: string;
51
+ };
52
+ /** A solution's scores, by axis (SCORES-1), as `dotreq scores create` makes them. */
53
+ export type Scores = {
54
+ costOfDelay: AxisScore;
55
+ relevance: AxisScore;
56
+ entities: AxisScore;
57
+ api: AxisScore;
58
+ testing: AxisScore;
59
+ experience: AxisScore;
60
+ doubt: AxisScore;
61
+ };
62
+ /** A thing as a drafter submits it: where it is, or scores by the id `createScores` gave. */
63
+ export type Submission = Exclude<Thing, {
64
+ kind: "scores";
65
+ }> | {
66
+ kind: "scores";
67
+ scoresId: string;
68
+ };
69
+ /** One outcome in a plan (`PlanOutcome` in packages/convex/solutions/plan/format.ts). */
70
+ export type PlanOutcome = {
71
+ name: string;
72
+ /** A surface's name, or another outcome's, as the scores are on the pitch. */
73
+ on: string;
74
+ awaits?: string[];
75
+ reviewAwaits?: string[];
76
+ against?: string[];
77
+ /** One of the plan's agents; left out, an agent dedicated to it: "<name>/drafter". */
78
+ drafter?: string;
79
+ /** The plan's agents; left out, one dedicated to it: "<name>/reviewer". Empty: no review. */
80
+ reviewers?: string[];
81
+ /** The agent the owner makes it with in a conversation. */
82
+ createdWith?: string;
83
+ /** Someone outside the product who makes it. */
84
+ createdBy?: string;
85
+ model?: string;
86
+ instructions?: string;
87
+ reviewInstructions?: string;
88
+ };
89
+ /** The plan in its stored shape (`OutcomePlan` in packages/convex/solutions/plan/format.ts). */
90
+ export type Plan = {
91
+ format: "outcomes";
92
+ inputs: string[];
93
+ surfaces: Array<{
94
+ name: string;
95
+ kind: string;
96
+ }>;
97
+ phases: Array<{
98
+ name: string;
99
+ class?: string;
100
+ outcomes: PlanOutcome[];
101
+ }>;
102
+ checkpoints: Array<{
103
+ name: string;
104
+ on: string;
105
+ presents?: string[];
106
+ }>;
107
+ agents: Array<{
108
+ name: string;
109
+ model?: string;
110
+ instructions?: string;
111
+ }>;
112
+ loops: Array<{
113
+ name: string;
114
+ iterations?: string[];
115
+ through: string[];
116
+ }>;
117
+ };
118
+ /**
119
+ * Part of a plan to rehearse (REHEARSE-1.0, 1.6): some of its outcomes,
120
+ * each by its name, run as though the outcomes before them were made.
121
+ */
122
+ export type PlanPart = {
123
+ from: Plan;
124
+ outcomes: string[];
125
+ };
126
+ /** What a script does when the agent it plays gets its brief. */
127
+ export type Script = {
128
+ /** Assert on the brief; throw to fail the rehearsal here (REHEARSE-2.1). */
129
+ expect?: (brief: unknown) => void;
130
+ /** Submit these things (drafting), with a comment saying what changed and why. */
131
+ made?: Submission[];
132
+ /** Make these scores and submit them (drafting scores), as `dotreq scores create` then `author scores:<id>` do, with the comment. */
133
+ scores?: Scores;
134
+ comment?: string;
135
+ /**
136
+ * With `made` or `scores`: the drafter's executive summary, which an
137
+ * outcome no agent reviews asks for (VERB-1.6). A script records as a
138
+ * real agent does (REHEARSE-1.1), so where the outcome asks for one and
139
+ * the script gives none, the work is refused.
140
+ */
141
+ summary?: string;
142
+ /**
143
+ * Give this review result (reviewing), with the comment every result
144
+ * carries. An approval, with or without revisions, carries an executive
145
+ * summary (VERB-2.5), and needs revision and escalate carry none; a
146
+ * script that breaks either is refused, as the agent would be
147
+ * (REHEARSE-1.1).
148
+ */
149
+ review?: {
150
+ outcome: "approved" | "approved with revisions" | "needs revision" | "escalate";
151
+ comment: string;
152
+ summary?: string;
153
+ };
154
+ /** Put a decision to the person instead (drafting). */
155
+ ask?: unknown;
156
+ /** Stall instead. */
157
+ stall?: string;
158
+ };
159
+ /** Real, or a script; a list plays one script per turn, the last repeating. */
160
+ export type Party = "real" | Script | Script[];
161
+ /**
162
+ * What the person answers, in order asked: by an outcome's name, her answer
163
+ * to a question or an escalation on it; by a checkpoint's name, her
164
+ * decision there, which a rehearsal gives only as an approval.
165
+ */
166
+ export type PersonScript = Record<string, Array<{
167
+ kind: "approval";
168
+ outcome: "approved" | "needs revision";
169
+ comment?: string;
170
+ } | {
171
+ kind: "choice";
172
+ option: string;
173
+ because?: string;
174
+ }>>;
175
+ /**
176
+ * The history a rehearsal starts from (REHEARSE-4.8): an earlier
177
+ * rehearsal's solution, the outcome it made that the new solution starts
178
+ * after, and the outcomes it holds as they stood then.
179
+ */
180
+ export type StartingHistory = {
181
+ solution: string;
182
+ after: string;
183
+ outcomes: string[];
184
+ };
185
+ /** A document published from a scenario's file for an input (REHEARSE-4.1). */
186
+ export type Published = {
187
+ documentId: string;
188
+ isRequirementsDoc: boolean;
189
+ };
190
+ export type Scenario = {
191
+ title: string;
192
+ /** Live only: the scenario file's name, recorded on the solution so the next run of it replaces this one (REHEARSE-6). */
193
+ file?: string;
194
+ /** What the person wrote about the solution beyond its title, as Markdown. */
195
+ description?: string;
196
+ /**
197
+ * The whole plan, or some of its outcomes run on their own (REHEARSE-1.0,
198
+ * 1.6).
199
+ */
200
+ plan: Plan | PlanPart;
201
+ /** The artifacts the plan's inputs hold, by outcome name. */
202
+ inputs?: Record<string, Thing[]>;
203
+ /**
204
+ * Live only: the documents published from the scenario's files for its
205
+ * inputs, each with whether its file's name makes it a requirements
206
+ * document (SOLDOC-3). Each comes to belong to the solution and the input
207
+ * that names it (REHEARSE-4.1.0).
208
+ */
209
+ published?: Published[];
210
+ /**
211
+ * Live only: the branch the solution's branch is made from, rather than
212
+ * the repository's default branch, such as one frozen with the output of
213
+ * the outcomes before the rehearsed ones (REHEARSE-4.5).
214
+ */
215
+ branch?: string;
216
+ /**
217
+ * Live only: an earlier rehearsal's solution and an outcome it made,
218
+ * whose history the solution starts with (REHEARSE-4.8), with the
219
+ * outcomes that solution holds, which may stand for inputs the scenario
220
+ * doesn't name.
221
+ */
222
+ history?: StartingHistory;
223
+ /**
224
+ * Who plays each agent (`partiesOf` in driver.ts): by agent, as
225
+ * `Code Reviewer` or `Pitch/drafter`; by an outcome's name, its drafter,
226
+ * or whoever outside makes it; by "<outcome>/reviewer", its reviewers; or
227
+ * by "<outcome>/<agent>", that agent on that outcome alone. Unnamed
228
+ * agents are real.
229
+ */
230
+ parties: Record<string, Party>;
231
+ person?: PersonScript;
232
+ /** How long to wait for a real agent, in ms (live only). */
233
+ waitMs?: number;
234
+ };
235
+ /** An outcome on the plan panel, as the driver reads it (`getPlan`). */
236
+ export type PanelOutcome = {
237
+ name: string;
238
+ status: string;
239
+ drafter?: string;
240
+ reviewers?: string[];
241
+ reason?: string;
242
+ /** Who it waits on, when someone outside makes it. */
243
+ party?: string;
244
+ };
245
+ /** The plan panel as the driver reads it: its outcomes by phase, and where each checkpoint stands. */
246
+ export type RehearsalPanel = {
247
+ phases: Array<{
248
+ outcomes: PanelOutcome[];
249
+ }>;
250
+ checkpoints?: Array<{
251
+ name: string;
252
+ stands: string;
253
+ }>;
254
+ };
255
+ /** The client the driver drives: convex-test in tests, the CLI client live. */
256
+ export type RehearsalClient = {
257
+ create(args: {
258
+ title: string;
259
+ /** The scenario file's name, when the rehearsal runs from one. */
260
+ scenario?: string;
261
+ description?: string;
262
+ plan: Plan;
263
+ inputs: Array<{
264
+ name: string;
265
+ things: Thing[];
266
+ }>;
267
+ /** The branch the solution's branch is made from, when the scenario names one. */
268
+ branch?: string;
269
+ /** The documents published from the scenario's files for its inputs. */
270
+ published?: Published[];
271
+ /** The earlier rehearsal whose history the solution starts with, when the scenario names one. */
272
+ history?: {
273
+ solution: string;
274
+ after: string;
275
+ };
276
+ }): Promise<string>;
277
+ /** The plan panel as it stands (`getPlan`). */
278
+ panel(solutionId: string): Promise<RehearsalPanel | null>;
279
+ /** What an agent picks up: its brief for the first part of an outcome's work it has due, or nothing ready. */
280
+ next(args: {
281
+ solutionId: string;
282
+ agent: string;
283
+ }): Promise<{
284
+ kind: string;
285
+ [key: string]: unknown;
286
+ }>;
287
+ /** Make scores, as `dotreq scores create` does: their id, to submit as `{ kind: "scores", scoresId }`. */
288
+ createScores(scores: Scores): Promise<string>;
289
+ /** Record an agent's work or result on an outcome; with no agent, someone outside closing it. */
290
+ complete(args: {
291
+ solutionId: string;
292
+ agent?: string;
293
+ outcome: string;
294
+ made?: Submission[];
295
+ comment?: string;
296
+ /** With `made`: the drafter's executive summary (VERB-1.6). */
297
+ summary?: string;
298
+ review?: {
299
+ outcome: "approved" | "approved with revisions" | "needs revision" | "escalate";
300
+ comment: string;
301
+ /** With an approval: the executive summary (VERB-2.5). */
302
+ summary?: string;
303
+ };
304
+ }): Promise<void>;
305
+ ask(args: {
306
+ solutionId: string;
307
+ agent: string;
308
+ outcome: string;
309
+ request: unknown;
310
+ }): Promise<void>;
311
+ stall(args: {
312
+ solutionId: string;
313
+ agent: string;
314
+ outcome: string;
315
+ reason: string;
316
+ }): Promise<void>;
317
+ /** The decision open on an outcome — a question or an escalation — or null. Absent when the client can't answer for the person. */
318
+ openDecision?(solutionId: string, outcome: string): Promise<{
319
+ id: string;
320
+ } | null>;
321
+ answer?(decisionId: string, answer: unknown): Promise<void>;
322
+ /** Approve a checkpoint's pass waiting on the person. Absent when the client can't decide for the person. */
323
+ approveCheckpoint?(solutionId: string, checkpoint: string, words?: string): Promise<void>;
324
+ thread(solutionId: string, outcome: string): Promise<unknown[]>;
325
+ things(solutionId: string, outcome: string): Promise<Thing[]>;
326
+ /** Live only: what a real agent did, from its sessions' logs. */
327
+ agentLog?(solutionId: string, agent: string): Promise<string | null>;
328
+ /** Live only: wait between polls. */
329
+ sleep?(ms: number): Promise<void>;
330
+ };
331
+ export type OutcomeReport = {
332
+ outcome: string;
333
+ status: string;
334
+ reason?: string;
335
+ things: Thing[];
336
+ thread: unknown[];
337
+ /** What each real agent on it did, from its log, by agent. */
338
+ agentLogs?: Record<string, string | null>;
339
+ };
340
+ /** What a live rehearsal did with the earlier runs of its scenario before it started (REHEARSE-6). */
341
+ export type EarlierRuns = {
342
+ /** The scenario file's name. */
343
+ scenario: string;
344
+ /** How many earlier runs it removed. */
345
+ removed: number;
346
+ /** Rehearsed with --keep: the earlier runs were left in place. */
347
+ keep?: boolean;
348
+ /** What an earlier run made that stayed: named by these scenario files, or, with none, used by another solution. */
349
+ kept: Array<{
350
+ reference: string;
351
+ namedBy: string[];
352
+ }>;
353
+ /** Earlier runs that stayed because these scenario files start from their history (REHEARSE-6.2.0). */
354
+ keptRuns?: Array<{
355
+ solutionId: string;
356
+ namedBy: string[];
357
+ }>;
358
+ /**
359
+ * The branches the earlier runs' solutions made (REHEARSE-6.7 to 6.10):
360
+ * deleted, already gone, kept and why, or still being deleted when the
361
+ * new run started.
362
+ */
363
+ branches?: Array<{
364
+ name: string;
365
+ outcome: "deleted" | "gone" | "kept" | "pending";
366
+ why?: string;
367
+ }>;
368
+ };
369
+ export type RehearsalReport = {
370
+ solutionId: string;
371
+ /** Live only: the earlier runs of its scenario, and what of theirs stayed. */
372
+ earlier?: EarlierRuns;
373
+ /** Why it ended: nothing ready, a decision nobody scripted, a stall, or a script's expectation. */
374
+ ended: string;
375
+ outcomes: OutcomeReport[];
376
+ };
377
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A rehearsal (docs/working/rehearsing-steps.md): a plan of outcomes, the
3
+ * artifacts its inputs hold, and who plays each agent the backend sends
4
+ * for — a real one, or a script that plays it from the brief the agent
5
+ * would get.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -26,6 +26,12 @@ export interface ParsedRequirementsFile {
26
26
  * Excludes example files, test fixtures, and build artifacts.
27
27
  */
28
28
  export declare function findRequirementsFiles(workspaceRoot: string): Promise<string[]>;
29
+ /**
30
+ * Every file a sync carries (SOLDOC-8): requirements files anywhere, plus
31
+ * the Markdown files directly inside solution directories. Other Markdown
32
+ * is left alone.
33
+ */
34
+ export declare function findSyncedFiles(workspaceRoot: string): Promise<string[]>;
29
35
  /**
30
36
  * Synchronous variant of {@link findRequirementsFiles}. Used by code paths
31
37
  * that can't await (e.g. the test-harness lookup-cache builder).
@@ -6,6 +6,8 @@ import { splitLeadingH1 } from "../schema/title-markdown.js";
6
6
  * Glob pattern matching *.requirements.md files.
7
7
  */
8
8
  const REQUIREMENTS_FILE_PATTERN = "**/*.requirements.md";
9
+ /** SOLDOC-8.1: a solution's other documents, directly inside a solution directory. */
10
+ const SOLUTION_DOCUMENT_PATTERN = "**/.requirements/solutions/*/*.md";
9
11
  /**
10
12
  * Directories ignored when discovering requirements files.
11
13
  * Shared between async and sync discovery so the two never drift.
@@ -15,6 +17,9 @@ const REQUIREMENTS_IGNORE_PATTERNS = [
15
17
  "**/dist/**",
16
18
  "**/.git/**",
17
19
  "**/build/**",
20
+ // Next.js's build output: a dev server's grows to gigabytes, and walking
21
+ // it made discovery take most of a minute.
22
+ "**/.next/**",
18
23
  // Exclude example and test fixture directories
19
24
  "**/example/**",
20
25
  "**/examples/**",
@@ -40,6 +45,25 @@ export async function findRequirementsFiles(workspaceRoot) {
40
45
  });
41
46
  return matches.map((m) => path.join(workspaceRoot, m));
42
47
  }
48
+ /**
49
+ * Every file a sync carries (SOLDOC-8): requirements files anywhere, plus
50
+ * the Markdown files directly inside solution directories. Other Markdown
51
+ * is left alone.
52
+ */
53
+ export async function findSyncedFiles(workspaceRoot) {
54
+ const [requirements, solutionDocs] = await Promise.all([
55
+ findRequirementsFiles(workspaceRoot),
56
+ glob(SOLUTION_DOCUMENT_PATTERN, {
57
+ cwd: workspaceRoot,
58
+ ignore: REQUIREMENTS_IGNORE_PATTERNS,
59
+ dot: true,
60
+ }),
61
+ ]);
62
+ const all = new Set(requirements);
63
+ for (const m of solutionDocs)
64
+ all.add(path.join(workspaceRoot, m));
65
+ return [...all];
66
+ }
43
67
  /**
44
68
  * Synchronous variant of {@link findRequirementsFiles}. Used by code paths
45
69
  * that can't await (e.g. the test-harness lookup-cache builder).
@@ -6,7 +6,7 @@
6
6
  export { buildRequirementMarkdown, buildRequirementsMarkdown, DEFAULT_DELIMITER, } from "./builder.js";
7
7
  export type { ConvexRequirement } from "./conversions.js";
8
8
  export { buildMetadata, constructKey, convexToRequirements, extractRequirementKeys, groupByRoot, parseKey, requirementsToConvex, } from "./conversions.js";
9
- export { DELIMITER_PATTERN, type ExtractedRequirementBlock, extractRequirementBlocks, findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementBlocksFromMarkdown, parseRootLine, ROOT_LABEL_MARKER, splitRequirementFenceContent, } from "./parser-core.js";
9
+ export { DELIMITER_PATTERN, type ExtractedRequirementBlock, extractRequirementBlocks, findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementBlocksFromMarkdown, parseRootLine, ROOT_LABEL_MARKER, splitRequirementFenceContent, stripFrontmatterBlock, } from "./parser-core.js";
10
10
  export type { Metadata, ParsedCriterion, RequirementKey, RequirementNode, RequirementPrefix, RequirementsFile, } from "./schemas.js";
11
11
  export { buildRequirementKey, canonicalRequirementKey, MetadataSchema, normalizePrefix, ParsedCriterionSchema, parseRequirementKey, REQUIREMENT_KEY_PATTERN, REQUIREMENT_PREFIX_PATTERN, RequirementKeySchema, RequirementNodeSchema, RequirementPrefixSchema, RequirementsFileSchema, ValidationError, validateKey, validateMetadata, validatePrefix, validateRequirementNode, validateRequirementsFile, } from "./schemas.js";
12
12
  export { composeMarkdownWithTitle, effectiveTitle, type SplitMarkdown, splitLeadingH1, } from "./title-markdown.js";
@@ -11,7 +11,9 @@ export { buildMetadata, constructKey, convexToRequirements, extractRequirementKe
11
11
  export { DELIMITER_PATTERN, extractRequirementBlocks, findRequirementById, flattenRequirementTree, getAllRequirements, parseCriterionLine, parseRequirementBlock, parseRequirementBlocksFromMarkdown, parseRootLine,
12
12
  // The web reaches parser-core only through this entry point, so without this
13
13
  // its readers have no way to import the marker and must hard-code it.
14
- ROOT_LABEL_MARKER, splitRequirementFenceContent, } from "./parser-core.js";
14
+ ROOT_LABEL_MARKER, splitRequirementFenceContent,
15
+ // A requirements file read from a repository in the browser (SOLDOC-12).
16
+ stripFrontmatterBlock, } from "./parser-core.js";
15
17
  // Schemas and types (uses zod - browser-safe)
16
18
  export { buildRequirementKey, canonicalRequirementKey,
17
19
  // Zod schemas
@@ -60,6 +60,11 @@ export interface OwnedFrontmatter {
60
60
  attachments?: AttachmentsDirective;
61
61
  /** The cloud version this content is at. */
62
62
  version: number;
63
+ /** SOLDOC-5: the belonging to write; absent means keep what the file says. */
64
+ belonging?: {
65
+ solutionId: string;
66
+ outcome: string;
67
+ };
63
68
  }
64
69
  /**
65
70
  * Write `body` to `filePath` under the file's own frontmatter, with the owned
@@ -50,6 +50,10 @@ export function writeRequirementsFile(filePath, owned, body) {
50
50
  frontmatter.setIn(["document", "attachments"],
51
51
  // Omit absent titles rather than writing `title: null` into the file.
52
52
  attachments.set.map((a) => a.title === undefined ? { url: a.url } : { url: a.url, title: a.title }));
53
+ if (owned.belonging) {
54
+ frontmatter.setIn(["document", "solution"], owned.belonging.solutionId);
55
+ frontmatter.setIn(["document", "outcome"], owned.belonging.outcome);
56
+ }
53
57
  // SYNC-TITLE-1.2: `document.title` is a recognized-but-retired field — the
54
58
  // title lives in the body as its leading H1 — so a rewrite drops it rather
55
59
  // than carrying it forward as if it were one of the user's own fields.
@@ -13,7 +13,14 @@ export { findRequirementById, flattenRequirementTree, getAllRequirements, parseC
13
13
  /**
14
14
  * Parse a complete requirements Markdown file.
15
15
  */
16
- export declare function parseRequirementsFile(markdownContent: string): {
16
+ export declare function parseRequirementsFile(markdownContent: string, options?: {
17
+ /**
18
+ * SOLDOC-3/4: a solution's other document (a pitch) carries no
19
+ * requirement blocks, and may carry no frontmatter yet, and is still a
20
+ * document.
21
+ */
22
+ solutionDocument?: boolean;
23
+ }): {
17
24
  metadata: RequirementsFile["_meta"];
18
25
  requirements: RequirementNode[];
19
26
  /** The markdown body (frontmatter stripped) — its leading H1 is the document's title (SYNC-TITLE-1). */
@@ -22,7 +29,9 @@ export declare function parseRequirementsFile(markdownContent: string): {
22
29
  /**
23
30
  * Parse requirements file from disk.
24
31
  */
25
- export declare function parseRequirementsFromFile(filePath: string): {
32
+ export declare function parseRequirementsFromFile(filePath: string, options?: {
33
+ solutionDocument?: boolean;
34
+ }): {
26
35
  metadata: RequirementsFile["_meta"];
27
36
  requirements: RequirementNode[];
28
37
  body: string;
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import * as fs from "node:fs";
11
11
  import YAML from "yaml";
12
- import { parseRequirementBlocksFromMarkdown, splitFrontmatter, } from "./parser-core.js";
12
+ import { parseRequirementBlocksFromMarkdown, splitFrontmatter, stripFrontmatterBlock, } from "./parser-core.js";
13
13
  import { ValidationError, validateMetadata, } from "./schemas.js";
14
14
  // Block/criterion parsing and tree utilities live in parser-core; re-exported
15
15
  // here so the schema module's public surface is unchanged.
@@ -18,11 +18,15 @@ export { findRequirementById, flattenRequirementTree, getAllRequirements, parseC
18
18
  * Extract YAML frontmatter from Markdown content.
19
19
  * Returns { frontmatter, body } where frontmatter is the parsed YAML object.
20
20
  */
21
- function extractFrontmatter(content) {
21
+ function extractFrontmatter(content, optional = false) {
22
22
  // Single fence match: yaml and body come from the same split, so they can
23
23
  // never disagree about where the frontmatter closes.
24
24
  const split = splitFrontmatter(content);
25
25
  if (split === undefined) {
26
+ // SOLDOC-4.0: a solution's document written by hand has no frontmatter
27
+ // yet; the push writes it after the first sync.
28
+ if (optional)
29
+ return { frontmatter: {}, body: stripFrontmatterBlock(content) };
26
30
  throw new ValidationError("Missing YAML frontmatter (should start with ---)");
27
31
  }
28
32
  let frontmatter;
@@ -37,14 +41,14 @@ function extractFrontmatter(content) {
37
41
  /**
38
42
  * Parse a complete requirements Markdown file.
39
43
  */
40
- export function parseRequirementsFile(markdownContent) {
44
+ export function parseRequirementsFile(markdownContent, options = {}) {
41
45
  // CRLF normalization (SYNC-FORMAT-1) happens inside the shared parser-core
42
46
  // helpers (splitFrontmatter / parseRequirementBlocksFromMarkdown), so every
43
47
  // entry point agrees.
44
- const { frontmatter, body } = extractFrontmatter(markdownContent);
48
+ const { frontmatter, body } = extractFrontmatter(markdownContent, options.solutionDocument);
45
49
  const metadata = validateMetadata(frontmatter);
46
50
  const requirements = parseRequirementBlocksFromMarkdown(body);
47
- if (requirements.length === 0) {
51
+ if (requirements.length === 0 && !options.solutionDocument) {
48
52
  throw new ValidationError("No requirement blocks found in document");
49
53
  }
50
54
  return { metadata, requirements, body };
@@ -52,10 +56,10 @@ export function parseRequirementsFile(markdownContent) {
52
56
  /**
53
57
  * Parse requirements file from disk.
54
58
  */
55
- export function parseRequirementsFromFile(filePath) {
59
+ export function parseRequirementsFromFile(filePath, options = {}) {
56
60
  try {
57
61
  const content = fs.readFileSync(filePath, "utf-8");
58
- return parseRequirementsFile(content);
62
+ return parseRequirementsFile(content, options);
59
63
  }
60
64
  catch (error) {
61
65
  if (error instanceof ValidationError) {