@turri-dev/cli 0.1.4-next.ebd337a → 0.1.4-next.fa9223b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/loop-cli.js CHANGED
@@ -85,6 +85,10 @@ async function runLoopReporting(options) {
85
85
  console.log(`${mark} ${job.id} ${pulseName(job)} - ${job.status}`);
86
86
  if (job.status !== "succeeded")
87
87
  console.log(c.dim((job.announce ?? job.report ?? job.error ?? "").trim()));
88
+ // Its report is not printed when it succeeds, and nobody is asked here: the spec gaps its agents met are said.
89
+ else
90
+ for (const gap of job.details?.specGaps ?? [])
91
+ console.log(c.yellow(` spec gap: ${gap.question}`), c.dim(`Meanwhile: ${gap.assumption}`));
88
92
  report?.write();
89
93
  },
90
94
  });
@@ -597,7 +597,14 @@ export function createWorkLoop(options) {
597
597
  }
598
598
  return {
599
599
  report: released !== undefined ? `${report}\n\n${released}` : report,
600
- details: { passed: result.landed, landed: result.landed, ...(released !== undefined ? { released: true } : {}), ...(result.story !== undefined ? { story: result.story } : {}) },
600
+ details: {
601
+ passed: result.landed,
602
+ landed: result.landed,
603
+ ...(released !== undefined ? { released: true } : {}),
604
+ ...(result.story !== undefined ? { story: result.story } : {}),
605
+ // The spec gaps its agents met, for a headless run to say - in the chat the report puts them to the person.
606
+ ...(result.specGaps !== undefined && result.specGaps.length > 0 ? { specGaps: result.specGaps } : {}),
607
+ },
601
608
  };
602
609
  },
603
610
  });
@@ -1565,7 +1572,7 @@ const defaultConformanceRunner = async (job, onDelta) => {
1565
1572
  writeTestsRecord(result.conformanceDir, job.planned.step.tree);
1566
1573
  recordHistory(job.buildDir, `conformance tests: ${job.planned.title}`);
1567
1574
  }
1568
- return { landed: result.passed, report: describeSpecDiffConformance(result) };
1575
+ return { landed: result.passed, report: describeSpecDiffConformance(result), ...(result.specGaps !== undefined ? { specGaps: result.specGaps } : {}) };
1569
1576
  };
1570
1577
  /** A job as a spec diff render, landed on its unit tests: conformance is a track of its own. */
1571
1578
  const defaultRunner = async (job, onDelta) => {
@@ -1581,7 +1588,9 @@ const defaultRunner = async (job, onDelta) => {
1581
1588
  onDelta,
1582
1589
  });
1583
1590
  // Landed code can be tried.
1584
- return { landed: result.passed, report: withTryItLine(describeSpecDiff(result), result.passed && result.restructured !== true), story: codeStory(result) };
1591
+ return { landed: result.passed, report: withTryItLine(describeSpecDiff(result), result.passed && result.restructured !== true), story: codeStory(result),
1592
+ ...(result.specGaps !== undefined ? { specGaps: result.specGaps } : {}),
1593
+ };
1585
1594
  };
1586
1595
  /** How a code pulse went, for its line in the notes: the rounds it took, its test runs, what the debugger found. */
1587
1596
  export function codeStory(result) {
@@ -323,20 +323,13 @@ function wrapped(text, width, colour = (line) => line) {
323
323
  .flatMap((line) => (line === "" ? [""] : wrapTextWithAnsi(line, Math.max(10, width - CONTINUED.length))))
324
324
  .map((line) => `${CONTINUED}${colour(line)}`);
325
325
  }
326
- /**
327
- * Whether a new item is drawn as an addition: only beside code already built - a spec diff that adds it - never when the
328
- * whole page is new, built for the first time or from scratch, where green would say nothing.
329
- */
330
- function addedToBuilt(item, page) {
331
- return item.state === "new" && page.items.some((other) => other.state === "built" || other.state === "changed" || other.state === "removed");
332
- }
333
326
  /** All of an item, opened: the rest of its text against the code's, and its acceptance tests. Where it stands is beside its mark. */
334
- function detailLines(item, width, style, added) {
327
+ function detailLines(item, width, style) {
335
328
  const lines = [];
336
329
  // The rest of its text - its first line is the row - against the record's, for a change.
337
330
  const rest = item.state === "changed" ? lineDiff(item.was ?? "", item.text).slice(1) : item.text.split("\n").slice(1).map((line) => ({ was: line, now: line }));
338
331
  for (const line of rest) {
339
- const drawn = item.state === "removed" ? style.removed(strike(line.now ?? "")) : item.state === "changed" ? diffLine(line, style) : added ? style.added(line.now ?? "") : highlighted(line.now ?? "", style);
332
+ const drawn = item.state === "removed" ? style.removed(strike(line.now ?? "")) : item.state === "changed" ? diffLine(line, style) : highlighted(line.now ?? "", style);
340
333
  lines.push(...wrapped(drawn, width));
341
334
  }
342
335
  if (item.acceptance.length > 0) {
@@ -483,9 +476,7 @@ export function specMapLines(pages, state, width, style, _jobs = [], now = Date.
483
476
  ? style.removed(strike(first))
484
477
  : item.state === "changed" && wasFirst !== first
485
478
  ? wordDiff(wasFirst, first, style)
486
- : addedToBuilt(item, page)
487
- ? style.added(first)
488
- : highlighted(first, style);
479
+ : highlighted(first, style);
489
480
  // Wrapped, never cut short: what follows a `…` is what a person came to read. The lines after the first hang
490
481
  // under the item's text, past its bullet.
491
482
  const [head = "", ...rest] = hanging(drawn, first, room);
@@ -499,7 +490,7 @@ export function specMapLines(pages, state, width, style, _jobs = [], now = Date.
499
490
  add(line, mark);
500
491
  }
501
492
  if (state.open.has(row.key)) {
502
- for (const detail of detailLines(item, width, style, addedToBuilt(item, page)))
493
+ for (const detail of detailLines(item, width, style))
503
494
  add(detail, mark);
504
495
  return;
505
496
  }
@@ -181,6 +181,18 @@ export function startRunReport(options) {
181
181
  list,
182
182
  };
183
183
  };
184
+ /** The spec gaps the pulses' agents met, in the order the pulses started: questions a person would want to decide. */
185
+ const specGapsOf = (jobs) => jobs.flatMap((job) => {
186
+ const details = job.details;
187
+ return (details?.specGaps ?? []).map((gap) => ({
188
+ module: job.module ?? null,
189
+ pulse: job.pulse ?? null,
190
+ pulse_id: job.id,
191
+ landed: job.status === "succeeded" && details?.landed === true,
192
+ question: gap.question,
193
+ assumption: gap.assumption,
194
+ }));
195
+ });
184
196
  const functionalitiesOf = (counts) => {
185
197
  if (counts === undefined)
186
198
  return null;
@@ -245,6 +257,7 @@ export function startRunReport(options) {
245
257
  usage: usageOf(jobs),
246
258
  pulses: pulsesOf(jobs, now),
247
259
  functionalities: functionalitiesOf(attempt(sources.completion)),
260
+ spec_gaps: specGapsOf(jobs),
248
261
  ...(exitParts ?? {}),
249
262
  logs: {
250
263
  jobs_dir: fromProject(join(projectRoot, ".turri", "jobs")),
package/docs/headless.md CHANGED
@@ -104,7 +104,8 @@ Conformance tests follow `conformance-tests:` in `config.turri.yaml`, or `--conf
104
104
  The command exits `0` only when every module is complete: its code regenerated from the spec as it is
105
105
  now, and its conformance tests written for what is built when they are on - no pulse failed, nothing
106
106
  halted, nothing waiting for a start over. Anything else exits `1`, and the last lines say which
107
- module is not complete, and why. The log says, per functionality, what was built and whether its
107
+ module is not complete, and why. A behaviour the spec leaves open that an agent had to decide is printed
108
+ as a `spec gap` under its pulse, with what was decided meanwhile. The log says, per functionality, what was built and whether its
108
109
  tests passed.
109
110
 
110
111
  | | |
@@ -191,6 +192,11 @@ changes meaning or goes; new fields may appear in the same version. Paths in `pu
191
192
  ],
192
193
  "codebases": ["plain_modules/app/code"]
193
194
  },
195
+ "spec_gaps": [{ // [] when no agent met one
196
+ "module": "app", "pulse": 3, "pulse_id": "2a058729", "landed": true,
197
+ "question": "What happens to a contact archived in HubSpot after it was synced?",
198
+ "assumption": "An archived contact is left as synced - asserted in sync.test.ts, archived contacts"
199
+ }],
194
200
  "build_dir_export": { "exported": true, "dir": "/work/out", "module": "app", "commit": "8036b3b", "files": 23 },
195
201
  "logs": { "jobs_dir": ".turri/jobs", "debug_dir": ".turri/debug", "sessions_dir": ".turri/sessions" }
196
202
  }
@@ -209,6 +215,13 @@ calls; a role or model turri could not tell is `unknown`.
209
215
  stopped, or did not pass - and everything it wrote was undone. A pulse that failed is tried once more
210
216
  before it counts as failed; both tries are the one pulse.
211
217
 
218
+ **Spec gaps.** A behaviour the spec leaves open that an agent met and decided on its own - reported with
219
+ `report_spec_gap` (see [regenerating](regenerating.md)). In the chat they are put to you as the pulse ends; a
220
+ headless run has no one to ask, so they are listed here, in the order the pulses started, and printed under the
221
+ pulse that met them. `assumption` is what the agent decided meanwhile, also in the codebase's notes'
222
+ assumptions while its pulse is `landed`; a pulse rolled back takes it out of the notes, but the question still
223
+ stands. Settle the ones you care about in the spec and run again.
224
+
212
225
  **Completed functional specs** - the pass rate, the figure to compare runs by.
213
226
  A functional spec is completed when the code record of the codebase the run delivers - the
214
227
  furthest-built of the chain, the one `--build-dir` exports - as last committed, holds it as the spec has
@@ -1111,7 +1111,9 @@ are the next agent's, and a gap is yours. An agent reports one with `report_spec
1111
1111
  and the assumption it made meanwhile; turri writes that assumption into the notes' assumptions, so the code built on
1112
1112
  it is known for what it is. The gaps a pulse's agents report are said in its report - "Spec gaps its agents met ... Put
1113
1113
  each to the user", each with its assumption - so the conversation asks you, and writes the ones you decide into the
1114
- spec; the pulse that builds the answer finds the assumption it replaces in the notes, and the tests it names. However many gaps a pulse's agents report, they
1114
+ spec; the pulse that builds the answer finds the assumption it replaces in the notes, and the tests it names.
1115
+ A headless run has no one to ask: it prints each gap under its pulse, and lists them in its run report's
1116
+ `spec_gaps` ([running unattended](headless.md#the-run-report)). However many gaps a pulse's agents report, they
1115
1117
  are put to you - never a reason to stop the pulse; the tool is guarded like any other. A notes
1116
1118
  file from before keeps no spec gaps section: its lines are dropped as it is read.
1117
1119
 
package/docs/using.md CHANGED
@@ -174,9 +174,7 @@ come waits for a free slot, and says so.
174
174
  regenerates. Pulses are numbered as they start in a module, from 1 each session.
175
175
 
176
176
  A changed item shows what changed in it: its first line word by word - what is gone struck through in red,
177
- what is new in green, what is kept highlighted as it reads now - and, under it, only the sub-bullets that changed. A new
178
- item beside code already built - one a spec diff adds - is green, all of it, so a new functional spec stands apart
179
- from one changed; where everything is new, built for the first time or from scratch, nothing is. A spec never built that is taken
177
+ what is new in green, what is kept highlighted as it reads now - and, under it, only the sub-bullets that changed. A spec never built that is taken
180
178
  out is simply gone. The top line has a strip of the module's functional specs - built, regenerating, planned,
181
179
  waiting - and how many are built, then what is happening: `pulse 3 of 8 regenerating`, or why nothing is -
182
180
  `not asked to build - regenerate plans it` (turri was just opened, or nothing has asked for the module: the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turri-dev/cli",
3
- "version": "0.1.4-next.ebd337a",
3
+ "version": "0.1.4-next.fa9223b",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {