@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
package/dist/convex.js CHANGED
@@ -25,6 +25,8 @@ export const api = {
25
25
  exportProject: query("documents/queries:exportProject"),
26
26
  // SHARE-TOKEN-3: the same export for a read-only share token
27
27
  exportProjectForShare: query("documents/queries:exportProjectForShare"),
28
+ // SYNC-FAIL-5: which of the workspace's ids name another project's document
29
+ foreignDocumentIds: query("documents/queries:foreignDocumentIds"),
28
30
  },
29
31
  mutations: {
30
32
  // SYNC-MODE-3: delete a cloud document from the CLI (repo-wins)
@@ -78,6 +80,68 @@ export const api = {
78
80
  ensureProjectShareToken: mutation("projectSecrets/mutations:ensureProjectShareToken"),
79
81
  },
80
82
  },
83
+ // VERB-7: scores made and revised from the shell, submitted as scores:<id>
84
+ scores: {
85
+ mutations: {
86
+ create: mutation("scores/mutations:create"),
87
+ update: mutation("scores/mutations:update"),
88
+ },
89
+ },
90
+ mockups: {
91
+ mutations: {
92
+ create: mutation("mockups/mutations:create"),
93
+ },
94
+ },
95
+ solutions: {
96
+ queries: {
97
+ getPlan: query("solutions/queries:getPlan"),
98
+ getThread: query("solutions/queries:getThread"),
99
+ getThings: query("solutions/queries:getThings"),
100
+ defaultPlan: query("solutions/queries:defaultPlan"),
101
+ readPlanFile: query("solutions/queries:readPlanFile"),
102
+ // An outcome's page: where it stands, and the decision open on it.
103
+ getOutcomePage: query("solutions/queries:getOutcomePage"),
104
+ getAgentLogs: query("solutions/queries:getAgentLogs"),
105
+ forSync: query("solutions/queries:forSync"),
106
+ rehearsalsOf: query("solutions/queries:rehearsalsOf"),
107
+ rehearsalHistory: query("solutions/queries:rehearsalHistory"),
108
+ },
109
+ mutations: {
110
+ create: mutation("solutions/mutations:create"),
111
+ start: mutation("solutions/mutations:start"),
112
+ next: mutation("solutions/mutations:next"),
113
+ complete: mutation("solutions/mutations:complete"),
114
+ ask: mutation("solutions/mutations:ask"),
115
+ stall: mutation("solutions/mutations:stall"),
116
+ answer: mutation("solutions/mutations:answer"),
117
+ // A rehearsal's scripted approval of a checkpoint (CHECKPOINT-1.1).
118
+ approveCheckpoint: mutation("solutions/mutations:approveCheckpoint"),
119
+ removeRehearsal: mutation("solutions/mutations:removeRehearsal"),
120
+ // SIGHT-7.0: where a video is uploaded before it is submitted
121
+ videoUploadUrl: mutation("solutions/mutations:videoUploadUrl"),
122
+ },
123
+ // SOLSHARE: a solution's share command, on its token alone.
124
+ shares: {
125
+ forCheckout: query("solutions/shares:forCheckout"),
126
+ push: mutation("solutions/shares:push"),
127
+ done: mutation("solutions/shares:done"),
128
+ },
129
+ // SIGHT-9: where an image is uploaded before it is submitted, and where
130
+ // the copies an outcome works on are fetched from.
131
+ images: {
132
+ uploadUrl: mutation("solutions/images:uploadUrl"),
133
+ // REHEARSE-4.7: an image a scenario names for an input, before the solution exists
134
+ inputUploadUrl: mutation("solutions/images:inputUploadUrl"),
135
+ copies: query("solutions/images:copies"),
136
+ },
137
+ // SOLBRANCH, REHEARSE-6.7: a solution's branch and its pushes.
138
+ branches: {
139
+ branchOf: query("solutions/branches:branchOf"),
140
+ claim: mutation("solutions/branches:claim"),
141
+ recordPush: mutation("solutions/branches:recordPush"),
142
+ removal: query("solutions/branches:removal"),
143
+ },
144
+ },
81
145
  lib: {
82
146
  authActions: {
83
147
  exchangeAuthorizationCode: action("lib/authActions:exchangeAuthorizationCode"),
package/dist/push/core.js CHANGED
@@ -35,8 +35,13 @@ export function parseFilesForPush(filePaths) {
35
35
  for (const filePath of filePaths) {
36
36
  // Read raw file content
37
37
  const rawContent = fs.readFileSync(filePath, "utf-8");
38
- // Parse to get metadata and requirements
39
- const parsed = parseRequirementsFromFile(filePath);
38
+ // Parse to get metadata and requirements. A file without the
39
+ // `.requirements.md` name is a solution's other document (SOLDOC-3): it
40
+ // has no requirement blocks, maybe no frontmatter yet, and is still a
41
+ // document.
42
+ const parsed = parseRequirementsFromFile(filePath, {
43
+ solutionDocument: !filePath.endsWith(".requirements.md"),
44
+ });
40
45
  const flatRequirements = getAllRequirements(parsed.requirements);
41
46
  // Extract markdown content (everything after frontmatter)
42
47
  const markdownContent = extractMarkdownContent(rawContent);
@@ -0,0 +1,23 @@
1
+ import type { Plan, PlanPart } from "./types.js";
2
+ /**
3
+ * Some of a plan's outcomes given their inputs (REHEARSE-1.0, 1.6, 1.8):
4
+ * the smallest plan holding those outcomes, in the plan's order and
5
+ * phases, whose inputs are what they await, are against or are on that
6
+ * none of them is, so they run as if the outcomes before them were made.
7
+ * So a scenario naming Scores alone, of the default plan, runs the scores
8
+ * with the pitch an input. An outcome on one left out is kept on that
9
+ * one's surface instead. The checkpoints on what is kept stay, presenting
10
+ * what is still there; loops go, since part of a plan starts or ends
11
+ * nothing.
12
+ *
13
+ * The one exception is the outcome that makes the solution exist: the
14
+ * plan's first outcome when the person makes it at creation, with an agent
15
+ * (the Idea in the default plan, which is the solution itself). When a
16
+ * chosen outcome works from it, it is kept at the head of the derived plan,
17
+ * so the solution is the idea there as in production, and the scenario
18
+ * names nothing for it.
19
+ */
20
+ export declare function planForOutcomes(plan: Plan, names: readonly string[]): Plan;
21
+ /** The plan a scenario names: the whole one, or some of its outcomes derived from it. */
22
+ export declare function planOf(plan: Plan | PlanPart): Plan;
23
+ //# sourceMappingURL=derive.d.ts.map
@@ -0,0 +1,112 @@
1
+ /** Every outcome in the plan, in its order. */
2
+ const outcomesOf = (plan) => plan.phases.flatMap((phase) => phase.outcomes);
3
+ /** The surface an outcome is kept on: its own, or the one the outcome it is on is kept on. */
4
+ function surfaceOf(plan, name) {
5
+ const seen = new Set();
6
+ let current = name;
7
+ for (;;) {
8
+ if (seen.has(current))
9
+ return undefined;
10
+ seen.add(current);
11
+ const outcome = outcomesOf(plan).find((each) => each.name === current);
12
+ if (!outcome) {
13
+ return plan.surfaces.find((surface) => surface.name === current)?.name;
14
+ }
15
+ if (plan.surfaces.some((surface) => surface.name === outcome.on)) {
16
+ return outcome.on;
17
+ }
18
+ current = outcome.on;
19
+ }
20
+ }
21
+ /** What an outcome waits on or works from: what it and its review await, what it is against, and the outcome it is on. */
22
+ const worksFrom = (plan, outcome) => [
23
+ ...(outcome.awaits ?? []),
24
+ ...(outcome.reviewAwaits ?? []),
25
+ ...(outcome.against ?? []),
26
+ ...(outcomesOf(plan).some((each) => each.name === outcome.on)
27
+ ? [outcome.on]
28
+ : []),
29
+ ];
30
+ /**
31
+ * Some of a plan's outcomes given their inputs (REHEARSE-1.0, 1.6, 1.8):
32
+ * the smallest plan holding those outcomes, in the plan's order and
33
+ * phases, whose inputs are what they await, are against or are on that
34
+ * none of them is, so they run as if the outcomes before them were made.
35
+ * So a scenario naming Scores alone, of the default plan, runs the scores
36
+ * with the pitch an input. An outcome on one left out is kept on that
37
+ * one's surface instead. The checkpoints on what is kept stay, presenting
38
+ * what is still there; loops go, since part of a plan starts or ends
39
+ * nothing.
40
+ *
41
+ * The one exception is the outcome that makes the solution exist: the
42
+ * plan's first outcome when the person makes it at creation, with an agent
43
+ * (the Idea in the default plan, which is the solution itself). When a
44
+ * chosen outcome works from it, it is kept at the head of the derived plan,
45
+ * so the solution is the idea there as in production, and the scenario
46
+ * names nothing for it.
47
+ */
48
+ export function planForOutcomes(plan, names) {
49
+ const all = outcomesOf(plan);
50
+ for (const name of names) {
51
+ if (!all.some((outcome) => outcome.name === name)) {
52
+ throw new Error(`The plan has no outcome called "${name}". Its outcomes: ${all.map((outcome) => outcome.name).join(", ")}.`);
53
+ }
54
+ }
55
+ const chosen = new Set(names);
56
+ const first = all[0];
57
+ const creation = first?.createdWith !== undefined && !chosen.has(first.name)
58
+ ? first
59
+ : undefined;
60
+ const picked = all.filter((outcome) => chosen.has(outcome.name));
61
+ const needed = [
62
+ ...new Set(picked.flatMap((outcome) => worksFrom(plan, outcome))),
63
+ ].filter((name) => !chosen.has(name));
64
+ const keepsCreation = !!creation && needed.includes(creation.name);
65
+ const kept = new Set([
66
+ ...chosen,
67
+ ...(keepsCreation && creation ? [creation.name] : []),
68
+ ]);
69
+ const inputs = [
70
+ ...plan.inputs.filter((name) => needed.includes(name)),
71
+ ...all
72
+ .map((outcome) => outcome.name)
73
+ .filter((name) => needed.includes(name) && !kept.has(name)),
74
+ ];
75
+ const phases = plan.phases.flatMap((phase) => {
76
+ const outcomes = phase.outcomes
77
+ .filter((outcome) => kept.has(outcome.name))
78
+ .map((outcome) =>
79
+ // On an outcome left out, it is kept on that one's surface.
80
+ all.some((each) => each.name === outcome.on) && !kept.has(outcome.on)
81
+ ? { ...outcome, on: surfaceOf(plan, outcome.on) ?? outcome.on }
82
+ : outcome);
83
+ return outcomes.length > 0 ? [{ ...phase, outcomes }] : [];
84
+ });
85
+ const known = new Set([...kept, ...inputs]);
86
+ const keptOn = (on) => kept.has(on) ||
87
+ [...kept].some((name) => surfaceOf(plan, name) === on && name !== on);
88
+ const checkpoints = plan.checkpoints.flatMap((checkpoint) => {
89
+ if (!keptOn(checkpoint.on))
90
+ return [];
91
+ if (!checkpoint.presents?.length)
92
+ return [checkpoint];
93
+ const presents = checkpoint.presents.filter((name) => known.has(name));
94
+ return presents.length > 0 ? [{ ...checkpoint, presents }] : [];
95
+ });
96
+ return {
97
+ format: "outcomes",
98
+ inputs,
99
+ surfaces: plan.surfaces,
100
+ phases,
101
+ checkpoints,
102
+ agents: plan.agents,
103
+ loops: [],
104
+ };
105
+ }
106
+ /** The plan a scenario names: the whole one, or some of its outcomes derived from it. */
107
+ export function planOf(plan) {
108
+ if (!("from" in plan))
109
+ return plan;
110
+ return planForOutcomes(plan.from, plan.outcomes);
111
+ }
112
+ //# sourceMappingURL=derive.js.map
@@ -0,0 +1,44 @@
1
+ import type { Party, Plan, RehearsalClient, RehearsalReport, Scenario } from "./types.js";
2
+ /**
3
+ * The plan a scenario rehearses, refused when it names an outcome the plan
4
+ * doesn't have or leaves one of its inputs holding nothing: what a live
5
+ * rehearsal checks before it removes the scenario's earlier runs, so a
6
+ * mistake in the scenario never costs the run under review. An input the
7
+ * scenario doesn't name holds something when the earlier rehearsal it
8
+ * starts from holds it (REHEARSE-4.8).
9
+ */
10
+ export declare function checkScenario(scenario: Scenario): Plan;
11
+ /** A part of an outcome's work: drafting it, reviewing it, or, with no agent, making it from outside. */
12
+ export type Part = {
13
+ outcome: string;
14
+ part: "draft" | "review" | "outside";
15
+ /** The agent; absent for an outcome someone outside, or the owner with an agent, makes. */
16
+ agent?: string;
17
+ /** Who makes it from outside, when the panel says. */
18
+ party?: string;
19
+ };
20
+ /**
21
+ * Who plays an agent at a part of an outcome's work in a scenario: the key
22
+ * naming that agent on that outcome (`Code/Code Reviewer`), then the key
23
+ * naming the part — the outcome's name for its drafter or whoever makes it
24
+ * from outside (`Pitch`), "<outcome>/reviewer" for its reviewers
25
+ * (`Pitch/reviewer`) — then the agent's own name (`Acceptance Tester`, or
26
+ * `Pitch/drafter` for one dedicated to its outcome), then whoever outside
27
+ * makes it (`release manager`). Unnamed, an agent is real. The key that
28
+ * matched, for counting turns.
29
+ */
30
+ export declare function partiesOf(parties: Scenario["parties"]): (at: Part) => {
31
+ party: Party;
32
+ key: string;
33
+ };
34
+ /**
35
+ * The driver (docs/working/rehearsing-steps.md): the orchestrator the
36
+ * product has as events, as a loop. Look at where every outcome stands;
37
+ * for each part of the work due, find who plays its agent; play a script
38
+ * through the same verbs an agent uses — `next` for the agent, then what
39
+ * its script records — or wait for a real one; answer decisions and
40
+ * checkpoints from the person's script; stop when nothing is ready, or
41
+ * when something needs a person nobody scripted, or an agent stalls.
42
+ */
43
+ export declare function rehearse(client: RehearsalClient, scenario: Scenario): Promise<RehearsalReport>;
44
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1,337 @@
1
+ import { planOf } from "./derive.js";
2
+ /**
3
+ * The plan a scenario rehearses, refused when it names an outcome the plan
4
+ * doesn't have or leaves one of its inputs holding nothing: what a live
5
+ * rehearsal checks before it removes the scenario's earlier runs, so a
6
+ * mistake in the scenario never costs the run under review. An input the
7
+ * scenario doesn't name holds something when the earlier rehearsal it
8
+ * starts from holds it (REHEARSE-4.8).
9
+ */
10
+ export function checkScenario(scenario) {
11
+ const plan = planOf(scenario.plan);
12
+ for (const name of plan.inputs) {
13
+ if (scenario.inputs?.[name])
14
+ continue;
15
+ if (scenario.history?.outcomes.includes(name))
16
+ continue;
17
+ throw new Error(scenario.history
18
+ ? `The plan's input "${name}" holds nothing: solution ${scenario.history.solution} held no "${name}" when it made "${scenario.history.after}", so name its artifacts in the scenario.`
19
+ : `The plan's input "${name}" holds nothing: name its artifacts in the scenario.`);
20
+ }
21
+ return plan;
22
+ }
23
+ /**
24
+ * Who plays an agent at a part of an outcome's work in a scenario: the key
25
+ * naming that agent on that outcome (`Code/Code Reviewer`), then the key
26
+ * naming the part — the outcome's name for its drafter or whoever makes it
27
+ * from outside (`Pitch`), "<outcome>/reviewer" for its reviewers
28
+ * (`Pitch/reviewer`) — then the agent's own name (`Acceptance Tester`, or
29
+ * `Pitch/drafter` for one dedicated to its outcome), then whoever outside
30
+ * makes it (`release manager`). Unnamed, an agent is real. The key that
31
+ * matched, for counting turns.
32
+ */
33
+ export function partiesOf(parties) {
34
+ return (at) => {
35
+ const keys = [
36
+ ...(at.agent ? [`${at.outcome}/${at.agent}`] : []),
37
+ at.part === "review" ? `${at.outcome}/reviewer` : at.outcome,
38
+ ...(at.agent ? [at.agent] : []),
39
+ ...(at.party ? [at.party] : []),
40
+ ];
41
+ const key = keys.find((name) => parties[name] !== undefined);
42
+ return key ? { party: parties[key], key } : { party: "real", key: keys[0] };
43
+ };
44
+ }
45
+ /** Every outcome on the panel, in the plan's order. */
46
+ const outcomesOn = (panel) => (panel?.phases ?? []).flatMap((phase) => phase.outcomes);
47
+ /**
48
+ * The parts of the work an agent, or someone outside, could take now or is
49
+ * at, in the plan's order: a ready or drafting outcome's draft, a review of
50
+ * an outcome standing reviewing (each of its reviewers, since the panel
51
+ * doesn't say which have given their result), and an outcome waiting on
52
+ * whoever makes it from outside.
53
+ */
54
+ function partsDue(outcomes) {
55
+ return outcomes.flatMap((outcome) => {
56
+ if ((outcome.status === "ready" || outcome.status === "drafting") &&
57
+ outcome.drafter) {
58
+ return [{ outcome: outcome.name, part: "draft", agent: outcome.drafter }];
59
+ }
60
+ if (outcome.status === "reviewing") {
61
+ return (outcome.reviewers ?? []).map((agent) => ({
62
+ outcome: outcome.name,
63
+ part: "review",
64
+ agent,
65
+ }));
66
+ }
67
+ if (outcome.status === "awaiting external" ||
68
+ outcome.status === "awaiting collaboration") {
69
+ return [
70
+ {
71
+ outcome: outcome.name,
72
+ part: "outside",
73
+ ...(outcome.party ? { party: outcome.party } : {}),
74
+ },
75
+ ];
76
+ }
77
+ return [];
78
+ });
79
+ }
80
+ /**
81
+ * The driver (docs/working/rehearsing-steps.md): the orchestrator the
82
+ * product has as events, as a loop. Look at where every outcome stands;
83
+ * for each part of the work due, find who plays its agent; play a script
84
+ * through the same verbs an agent uses — `next` for the agent, then what
85
+ * its script records — or wait for a real one; answer decisions and
86
+ * checkpoints from the person's script; stop when nothing is ready, or
87
+ * when something needs a person nobody scripted, or an agent stalls.
88
+ */
89
+ export async function rehearse(client, scenario) {
90
+ const plan = checkScenario(scenario);
91
+ const inputs = Object.entries(scenario.inputs ?? {}).map(([name, things]) => ({ name, things }));
92
+ const solutionId = await client.create({
93
+ title: scenario.title,
94
+ scenario: scenario.file,
95
+ description: scenario.description,
96
+ plan,
97
+ inputs,
98
+ ...(scenario.branch ? { branch: scenario.branch } : {}),
99
+ ...(scenario.published?.length ? { published: scenario.published } : {}),
100
+ ...(scenario.history
101
+ ? {
102
+ history: {
103
+ solution: scenario.history.solution,
104
+ after: scenario.history.after,
105
+ },
106
+ }
107
+ : {}),
108
+ });
109
+ const partyFor = partiesOf(scenario.parties);
110
+ const played = {};
111
+ /** The script for this turn: the n-th of a list, the last repeating, counted by outcome and agent. */
112
+ const scriptFor = (at, party) => {
113
+ if (!Array.isArray(party))
114
+ return party;
115
+ const key = `${at.outcome}/${at.agent ?? at.part}`;
116
+ const n = played[key] ?? 0;
117
+ played[key] = n + 1;
118
+ return party[Math.min(n, party.length - 1)];
119
+ };
120
+ const asked = {};
121
+ /** The person's next scripted answer under a name, taken. */
122
+ const answerFor = (name) => {
123
+ const answers = scenario.person?.[name] ?? [];
124
+ const n = asked[name] ?? 0;
125
+ const answer = answers[n];
126
+ if (answer)
127
+ asked[name] = n + 1;
128
+ return answer;
129
+ };
130
+ const waited = {};
131
+ const waitMs = scenario.waitMs ?? 20 * 60 * 1000;
132
+ const pollMs = 15_000;
133
+ let ended = "nothing ready";
134
+ for (let turn = 0; turn < 500; turn++) {
135
+ const panel = await client.panel(solutionId);
136
+ const outcomes = outcomesOn(panel);
137
+ // A decision waiting: the person's script answers, or the rehearsal ends.
138
+ const deciding = outcomes.find((o) => o.status === "awaiting decision");
139
+ if (deciding) {
140
+ const decision = await client.openDecision?.(solutionId, deciding.name);
141
+ const answer = decision && client.answer ? answerFor(deciding.name) : undefined;
142
+ if (!answer || !decision || !client.answer) {
143
+ ended = `"${deciding.name}" waits on a decision nobody scripted`;
144
+ break;
145
+ }
146
+ await client.answer(decision.id, answer);
147
+ continue;
148
+ }
149
+ const stalled = outcomes.find((o) => o.status === "stalled");
150
+ if (stalled) {
151
+ ended = `"${stalled.name}" stalled: ${stalled.reason ?? "no reason given"}`;
152
+ break;
153
+ }
154
+ // A checkpoint waiting on the person: her scripted approval, or the end.
155
+ const checkpoint = panel?.checkpoints?.find((c) => c.stands === "deciding");
156
+ if (checkpoint) {
157
+ const answer = client.approveCheckpoint
158
+ ? answerFor(checkpoint.name)
159
+ : undefined;
160
+ if (!answer || !client.approveCheckpoint) {
161
+ ended = `the ${checkpoint.name} checkpoint waits on a decision nobody scripted`;
162
+ break;
163
+ }
164
+ if (answer.kind !== "approval" || answer.outcome !== "approved") {
165
+ throw new Error(`The person's script answers the ${checkpoint.name} checkpoint with an approval: { kind: approval, outcome: approved }, with any comment as her words.`);
166
+ }
167
+ await client.approveCheckpoint(solutionId, checkpoint.name, answer.comment);
168
+ continue;
169
+ }
170
+ // The parts due, scripted ones first: a script takes its agent's work
171
+ // through `next`, as the agent would.
172
+ const due = partsDue(outcomes).map((at) => ({ at, ...partyFor(at) }));
173
+ let acted = false;
174
+ for (const { at, party } of due) {
175
+ if (party === "real")
176
+ continue;
177
+ if (at.part === "outside") {
178
+ await playOutside(client, solutionId, at, scriptFor(at, party));
179
+ acted = true;
180
+ break;
181
+ }
182
+ const brief = await client.next({ solutionId, agent: at.agent });
183
+ if (brief.kind === "nothing ready")
184
+ continue;
185
+ const taken = takenPart(brief, at.agent);
186
+ const script = partyFor(taken);
187
+ if (script.party === "real") {
188
+ throw new Error(`${at.agent} is scripted for "${at.outcome}" but real for "${taken.outcome}", which it took first: script it for both, or neither.`);
189
+ }
190
+ await play(client, solutionId, taken, scriptFor(taken, script.party), brief);
191
+ acted = true;
192
+ break;
193
+ }
194
+ if (acted)
195
+ continue;
196
+ // Nothing a script could take: wait for a real agent at work or due.
197
+ const real = due
198
+ .filter(({ at, party }) => party === "real" && at.part !== "outside")
199
+ .map(({ at }) => ({ outcome: at.outcome, agent: at.agent }))[0];
200
+ if (!real) {
201
+ ended = "nothing ready";
202
+ break;
203
+ }
204
+ if (!client.sleep) {
205
+ throw new Error(`${real.agent} is real on "${real.outcome}", and only a live rehearsal can wait for a real agent.`);
206
+ }
207
+ const key = `${real.outcome}/${real.agent}`;
208
+ waited[key] = (waited[key] ?? 0) + pollMs;
209
+ if (waited[key] > waitMs) {
210
+ ended = `"${real.outcome}" waited ${Math.round(waitMs / 60000)} minutes for ${real.agent}`;
211
+ break;
212
+ }
213
+ await client.sleep(pollMs);
214
+ }
215
+ const reports = [];
216
+ for (const outcome of outcomesOn(await client.panel(solutionId))) {
217
+ const report = {
218
+ outcome: outcome.name,
219
+ status: outcome.status,
220
+ reason: outcome.reason,
221
+ things: await client.things(solutionId, outcome.name),
222
+ thread: await client.thread(solutionId, outcome.name),
223
+ };
224
+ if (client.agentLog) {
225
+ const agents = [
226
+ ...(outcome.drafter ? [outcome.drafter] : []),
227
+ ...(outcome.reviewers ?? []),
228
+ ];
229
+ const logs = {};
230
+ for (const agent of agents) {
231
+ const part = {
232
+ outcome: outcome.name,
233
+ part: agent === outcome.drafter ? "draft" : "review",
234
+ agent,
235
+ };
236
+ if (partyFor(part).party === "real") {
237
+ logs[agent] = await client.agentLog(solutionId, agent);
238
+ }
239
+ }
240
+ if (Object.keys(logs).length > 0)
241
+ report.agentLogs = logs;
242
+ }
243
+ reports.push(report);
244
+ }
245
+ return { solutionId, ended, outcomes: reports };
246
+ }
247
+ /** The part a brief hands the agent: the outcome it names, drafting or reviewing it. */
248
+ function takenPart(brief, agent) {
249
+ const step = brief.step;
250
+ const outcome = step?.outcome ?? step?.label;
251
+ if (!outcome) {
252
+ throw new Error(`${agent} was given a brief that names no outcome.`);
253
+ }
254
+ return { outcome, part: brief.kind === "review" ? "review" : "draft", agent };
255
+ }
256
+ /** A script for an agent: check the brief it was given as the agent would read it, then act. */
257
+ async function play(client, solutionId, at, script, brief) {
258
+ const agent = at.agent;
259
+ const { outcome } = at;
260
+ const where = `"${outcome}" (${agent}, ${at.part === "review" ? "reviewing" : "drafting"})`;
261
+ if (script.expect) {
262
+ try {
263
+ script.expect(brief);
264
+ }
265
+ catch (error) {
266
+ throw new Error(`${where}: the brief isn't what the script expected — ${error instanceof Error ? error.message : String(error)}`);
267
+ }
268
+ }
269
+ if (script.stall) {
270
+ await client.stall({ solutionId, agent, outcome, reason: script.stall });
271
+ return;
272
+ }
273
+ if (script.ask) {
274
+ await client.ask({ solutionId, agent, outcome, request: script.ask });
275
+ return;
276
+ }
277
+ // REHEARSE-1.1: a script records exactly as a real agent does, so an
278
+ // approval carries the summary the script gives (VERB-2.5) and none
279
+ // stands in for a missing one; the backend refuses it as it would the
280
+ // agent's.
281
+ if (at.part === "review") {
282
+ if (!script.review) {
283
+ throw new Error(`${where}: the script gives no review — give it review, ask or stall.`);
284
+ }
285
+ await client.complete({
286
+ solutionId,
287
+ agent,
288
+ outcome,
289
+ review: script.review,
290
+ });
291
+ return;
292
+ }
293
+ const made = await madeBy(client, script);
294
+ if (!made) {
295
+ throw new Error(`${where}: the script says nothing to do — give it made, scores, ask or stall.`);
296
+ }
297
+ await client.complete({
298
+ solutionId,
299
+ agent,
300
+ outcome,
301
+ made,
302
+ comment: script.comment,
303
+ summary: script.summary,
304
+ });
305
+ }
306
+ /** What a script submits: its things, or the scores it makes first, as `dotreq scores create` would. */
307
+ async function madeBy(client, script) {
308
+ if (script.made)
309
+ return script.made;
310
+ if (script.scores) {
311
+ return [
312
+ {
313
+ kind: "scores",
314
+ scoresId: await client.createScores(script.scores),
315
+ },
316
+ ];
317
+ }
318
+ return undefined;
319
+ }
320
+ /**
321
+ * A script for whoever makes an outcome from outside the product, or the
322
+ * owner with an agent: it closes the outcome with what it made, as their
323
+ * close would (AWAIT-7.1: a rehearsal's scripts play every party).
324
+ */
325
+ async function playOutside(client, solutionId, at, script) {
326
+ const made = await madeBy(client, script);
327
+ if (!made) {
328
+ throw new Error(`"${at.outcome}" (${at.party ?? "made outside"}): the script says nothing to do — give it made or scores.`);
329
+ }
330
+ await client.complete({
331
+ solutionId,
332
+ outcome: at.outcome,
333
+ made,
334
+ comment: script.comment,
335
+ });
336
+ }
337
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1,25 @@
1
+ import type { solutionClient } from "../commands/solution/client.js";
2
+ import type { EarlierRuns } from "./types.js";
3
+ /**
4
+ * A new rehearsal replaces the earlier runs of its scenario (REHEARSE-6):
5
+ * the rehearsal solutions in the project recorded as runs of the same
6
+ * scenario file are removed before it starts, with what they made. What any scenario in the
7
+ * same folder names as an input stays, so that scenario can still start
8
+ * from it; so does an earlier run whose history a scenario there starts
9
+ * from (6.2.0), whole.
10
+ */
11
+ /**
12
+ * What the scenarios in this scenario's folder name: the documents, boards
13
+ * and mockups they name as inputs, and the earlier rehearsals' solutions
14
+ * whose history they start from (REHEARSE-6.2.0), each by id, with the files
15
+ * that name it. Refuses when one of those files can't be read, since then
16
+ * what it names can't be known.
17
+ */
18
+ export declare function namedInputs(scenarioPath: string): Map<string, string[]>;
19
+ /**
20
+ * Remove the earlier runs of the scenario, keeping what a scenario names
21
+ * (REHEARSE-6.0, 6.2), and keeping whole an earlier run whose history a
22
+ * scenario starts from (6.2.0).
23
+ */
24
+ export declare function removeEarlierRuns(scenarioPath: string, client: ReturnType<typeof solutionClient>, pause?: (ms: number) => Promise<void>): Promise<EarlierRuns>;
25
+ //# sourceMappingURL=earlier.d.ts.map