@turri-dev/cli 0.1.8 → 0.1.9

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.
@@ -82,7 +82,10 @@ export declare const MAX_SPECS_PER_JOB = 3;
82
82
  /** The most a pulse's title is - in words, and in characters - however it was made: a host shows it as its heading. */
83
83
  export declare const TITLE_WORDS = 10;
84
84
  export declare const TITLE_CHARS = 70;
85
- /** A title held to `TITLE_WORDS` and `TITLE_CHARS` - or less, for room beside it - cut at a word, with an ellipsis. */
85
+ /**
86
+ * A title held to `TITLE_WORDS` and `TITLE_CHARS` - or less, for room beside it - cut at a word, with an ellipsis. Text
87
+ * cut already is cut again without stacking a second: several first lines joined, each cut, end on one ellipsis.
88
+ */
86
89
  export declare function shortTitle(text: string, chars?: number, words?: number): string;
87
90
  export declare const MAX_LINES_PER_JOB = 120;
88
91
  /**
@@ -140,22 +143,39 @@ export interface PlaceAnswer {
140
143
  /** What of the person's wishes it did not follow, and why. */
141
144
  notes?: string;
142
145
  }
146
+ /** A group to title: the number the agent answers with, and its functional specs' text. */
147
+ export interface TitleGroup {
148
+ number: number;
149
+ specs: string[];
150
+ }
151
+ export interface TitleAnswer {
152
+ titles: Array<{
153
+ group: number;
154
+ title: string;
155
+ }>;
156
+ }
143
157
  /**
144
- * The two judgements, each asked again with what was wrong with its answer - the last answer given, whether it holds
145
- * or not: the plan checks it again. Injectable, so the plan is testable without a model.
158
+ * The judgements, each asked again with what was wrong with its answer - the last answer given, whether it holds
159
+ * or not: the plan checks it again. Injectable, so the plan is testable without a model. `title` titles the groups no
160
+ * agent titled - the conformance tests', and a grouping that fell back - for what each pulse does, rather than its
161
+ * first functional spec's first line; without it, they keep that.
146
162
  */
147
163
  export interface ChunkAgents {
148
164
  group(specs: NewSpec[], check: (answer: GroupAnswer) => string[], steering?: Steering[], maxSpecs?: number): Promise<GroupAnswer | undefined>;
149
165
  place(input: PlaceInput, check: (answer: PlaceAnswer) => string[]): Promise<PlaceAnswer | undefined>;
166
+ title?(groups: TitleGroup[], check: (answer: TitleAnswer) => string[], track: "code" | "conformance"): Promise<TitleAnswer | undefined>;
150
167
  }
151
168
  /** What is wrong with a grouping: every new functional spec once, in order, next to each other, none too big. */
152
169
  export declare function groupProblems(specs: NewSpec[], answer: GroupAnswer, maxSpecs?: number): string[];
170
+ /** What is wrong with a titling: every group titled once, by its number, none empty, none too long. */
171
+ export declare function titleProblems(groups: TitleGroup[], answer: TitleAnswer): string[];
153
172
  /** What is wrong with a placement: every new req once, each where there is such a job. */
154
173
  export declare function placeProblems(input: PlaceInput, answer: PlaceAnswer): string[];
155
174
  export declare function planChunks(input: PlanInput, agents?: ChunkAgents, notes?: string[]): Promise<DraftJob[]>;
156
175
  export declare function groupPrompt(specs: NewSpec[], steering?: Steering[], maxSpecs?: number): string;
157
176
  export declare function placePrompt(input: PlaceInput): string;
158
- /** The chunking's two agents, on a model - fresh for every plan. */
177
+ export declare function titlePrompt(groups: TitleGroup[], track: "code" | "conformance"): string;
178
+ /** The chunking's agents, on a model - fresh for every plan. */
159
179
  export declare function llmChunkAgents(options: {
160
180
  cwd: string;
161
181
  model?: string;
@@ -74,15 +74,23 @@ export const MAX_SPECS_PER_JOB = 3;
74
74
  /** The most a pulse's title is - in words, and in characters - however it was made: a host shows it as its heading. */
75
75
  export const TITLE_WORDS = 10;
76
76
  export const TITLE_CHARS = 70;
77
- /** A title held to `TITLE_WORDS` and `TITLE_CHARS` - or less, for room beside it - cut at a word, with an ellipsis. */
77
+ /** Whether a title is within `TITLE_WORDS` and `TITLE_CHARS`. */
78
+ function fitsTitle(title) {
79
+ return title.trim().split(/\s+/).length <= TITLE_WORDS && title.trim().length <= TITLE_CHARS;
80
+ }
81
+ /**
82
+ * A title held to `TITLE_WORDS` and `TITLE_CHARS` - or less, for room beside it - cut at a word, with an ellipsis. Text
83
+ * cut already is cut again without stacking a second: several first lines joined, each cut, end on one ellipsis.
84
+ */
78
85
  export function shortTitle(text, chars = TITLE_CHARS, words = TITLE_WORDS) {
79
86
  const all = text.trim().split(/\s+/).filter((word) => word !== "");
80
87
  let cut = all.slice(0, words).join(" ");
81
88
  if (all.length <= words && cut.length <= chars)
82
89
  return cut;
90
+ cut = cut.replace(/[\s,.;:…-]+$/, "");
83
91
  if (cut.length > chars - 1)
84
92
  cut = cut.slice(0, chars - 1).replace(/\s+\S*$/, "") || cut.slice(0, chars - 1);
85
- return `${cut.replace(/[\s,.;:-]+$/, "")}…`;
93
+ return `${cut.replace(/[\s,.;:…-]+$/, "")}…`;
86
94
  }
87
95
  export const MAX_LINES_PER_JOB = 120;
88
96
  /** How often an answer that does not hold goes back to its agent before the plan falls back. */
@@ -137,9 +145,31 @@ export function groupProblems(specs, answer, maxSpecs = MAX_SPECS_PER_JOB) {
137
145
  }
138
146
  if (group.title.trim() === "")
139
147
  problems.push("Every group needs a title.");
140
- else if (group.title.trim().split(/\s+/).length > TITLE_WORDS || group.title.trim().length > TITLE_CHARS) {
148
+ else if (!fitsTitle(group.title))
141
149
  problems.push(`"${group.title}" is too long for a title: at most ${TITLE_WORDS} words and ${TITLE_CHARS} characters.`);
142
- }
150
+ }
151
+ return problems;
152
+ }
153
+ /** What is wrong with a titling: every group titled once, by its number, none empty, none too long. */
154
+ export function titleProblems(groups, answer) {
155
+ const problems = [];
156
+ const numbers = groups.map((group) => group.number);
157
+ const seen = new Map();
158
+ for (const entry of answer.titles) {
159
+ if (!numbers.includes(entry.group))
160
+ problems.push(`There is no group ${entry.group}.`);
161
+ seen.set(entry.group, (seen.get(entry.group) ?? 0) + 1);
162
+ if (entry.title.trim() === "")
163
+ problems.push(`Group ${entry.group} needs a title.`);
164
+ else if (!fitsTitle(entry.title))
165
+ problems.push(`"${entry.title}" is too long for a title: at most ${TITLE_WORDS} words and ${TITLE_CHARS} characters.`);
166
+ }
167
+ for (const number of numbers) {
168
+ const times = seen.get(number) ?? 0;
169
+ if (times === 0)
170
+ problems.push(`Group ${number} has no title: every group is titled.`);
171
+ if (times > 1)
172
+ problems.push(`Group ${number} is titled ${times} times: once.`);
143
173
  }
144
174
  return problems;
145
175
  }
@@ -271,8 +301,8 @@ function plannerFailed(step, error) {
271
301
  export async function planChunks(input, agents, notes) {
272
302
  if (input.track !== "conformance")
273
303
  return cut(input, agents, notes);
274
- // The conformance tests are grouped by the harness, as one job as far as the guardrail lets: no agent is asked.
275
- return anchored(await cut(input), netChanges(input.base, input.current));
304
+ // The conformance tests are grouped by the harness, as one job as far as the guardrail lets: an agent only titles them.
305
+ return anchored(await cut(input, agents, notes), netChanges(input.base, input.current));
276
306
  }
277
307
  /**
278
308
  * A conformance plan, every job of it testing a functional spec: a job that touches none - reqs or definitions alone -
@@ -393,8 +423,29 @@ async function cut(input, agents, notes) {
393
423
  ...list.map((group) => ({ title: group.title, specs: group.specs.map((number) => bySpec.get(number)), definitions: [], reqs: [] })),
394
424
  ];
395
425
  const conformance = input.track === "conformance";
426
+ // The conformance tests are grouped and their reqs placed by the harness: their agents are only asked for titles.
427
+ const planners = conformance ? undefined : agents;
428
+ /**
429
+ * Groups titled for what their pulses do, by the title agent: those no agent titled - the conformance tests', a
430
+ * grouping that fell back. When it fails, or its answer does not hold, they keep their first lines.
431
+ */
432
+ const titled = async (list) => {
433
+ if (agents?.title === undefined || list.length === 0)
434
+ return list;
435
+ const track = conformance ? "conformance" : "code";
436
+ reportStage(`planning the queue: a title for each of the ${list.length} pulse${list.length === 1 ? "" : "s"}`);
437
+ const toTitle = list.map((group, index) => ({ number: index + 1, specs: group.specs.map((number) => textOf(bySpec.get(number))) }));
438
+ const answer = (await agents.title(toTitle, (given) => titleProblems(toTitle, given), track).catch((error) => plannerFailed("title", error))) ?? undefined;
439
+ const problems = answer === undefined ? [] : titleProblems(toTitle, answer);
440
+ if (answer !== undefined && problems.length === 0)
441
+ return list.map((group, index) => ({ ...group, title: answer.titles.find((entry) => entry.group === index + 1).title.trim() }));
442
+ record({ kind: "planner.fellBack", step: "title", why: answer === undefined ? "no titles were given" : firstLine(problems.join(" "), 300) });
443
+ say(answer === undefined ? "the titles were not given - each pulse titled by its functional specs' first lines" : `the titles did not hold (${problems.join(" ")}) - each pulse titled by its functional specs' first lines`);
444
+ return list;
445
+ };
396
446
  const grouped = conformance ? undefined : ((await agents?.group(numbered, (answer) => groupProblems(numbered, answer, maxSpecs), input.steering, maxSpecs).catch((error) => plannerFailed("group", error))) ?? undefined);
397
- const groups = conformance
447
+ const holds = grouped !== undefined && groupProblems(numbered, grouped, maxSpecs).length === 0;
448
+ const made = conformance
398
449
  ? conformanceGroups(numbered, input.separately === true, maxLines, (list) => {
399
450
  const trial = chunksFor(list);
400
451
  placeDefinitions(trial, definitions);
@@ -403,7 +454,7 @@ async function cut(input, agents, notes) {
403
454
  const frid = fridOf(bySpec.get(spec.number));
404
455
  return frid === undefined ? -1 : (input.groups ?? []).findIndex((group) => group.includes(frid));
405
456
  })
406
- : grouped !== undefined && groupProblems(numbered, grouped, maxSpecs).length === 0
457
+ : grouped !== undefined && holds
407
458
  ? grouped.groups
408
459
  : numbered.map((spec, index) => ({ specs: [spec.number], title: titleOf(specs[index]) }));
409
460
  if (grouped?.notes !== undefined && grouped.notes.trim() !== "")
@@ -418,6 +469,8 @@ async function cut(input, agents, notes) {
418
469
  say(`the grouping did not hold (${groupProblems(numbered, grouped, maxSpecs).join(" ")}) - a functional spec a pulse`);
419
470
  }
420
471
  }
472
+ // Titled before the chunks are made of them: a group split for its size keeps its title, numbered.
473
+ const groups = holds ? made : await titled(made);
421
474
  let chunks = chunksFor(groups);
422
475
  // 2. The definitions each group uses.
423
476
  placeDefinitions(chunks, definitions);
@@ -434,13 +487,13 @@ async function cut(input, agents, notes) {
434
487
  implementationReqs: implementationReqs.map((change, index) => ({ number: index + 1, text: textOf(change) })),
435
488
  testReqs: testReqs.map((change, index) => ({ number: index + 1, text: textOf(change) })),
436
489
  };
437
- const answer = (await agents?.place(placeInput, (given) => placeProblems(placeInput, given)).catch((error) => plannerFailed("place", error))) ?? undefined;
490
+ const answer = (await planners?.place(placeInput, (given) => placeProblems(placeInput, given)).catch((error) => plannerFailed("place", error))) ?? undefined;
438
491
  if (answer?.notes !== undefined && answer.notes.trim() !== "")
439
492
  say(`the placement: ${answer.notes.trim()}`);
440
493
  placement = answer !== undefined && placeProblems(placeInput, answer).length === 0 ? answer : undefined;
441
- if (placement === undefined && agents !== undefined)
494
+ if (placement === undefined && planners !== undefined)
442
495
  record({ kind: "planner.fellBack", step: "place", why: answer === undefined ? "no placement was given" : firstLine(placeProblems(placeInput, answer).join(" "), 300) });
443
- if (placement === undefined && agents !== undefined)
496
+ if (placement === undefined && planners !== undefined)
444
497
  say("the placement did not hold - every implementation req with the first pulse, every test req with the first conformance pulse");
445
498
  }
446
499
  /** A req's chunk: its group, the built job, or the first job of the new work for "all". */
@@ -489,7 +542,7 @@ async function cut(input, agents, notes) {
489
542
  say(`"${group.title}" is past ${maxLines} lines with its definitions and reqs - split into ${runs.length} pulses`);
490
543
  runs.forEach((specs, index) => split.push({
491
544
  specs,
492
- title: specs.length === 1 ? titleOf(bySpec.get(specs[0])) : `${shortTitle(group.title, TITLE_CHARS - 9, TITLE_WORDS - 3)} (${index + 1} of ${runs.length})`,
545
+ title: `${shortTitle(group.title, TITLE_CHARS - 9, TITLE_WORDS - 3)} (${index + 1} of ${runs.length})`,
493
546
  }));
494
547
  });
495
548
  const before = placement;
@@ -517,7 +570,7 @@ async function cut(input, agents, notes) {
517
570
  // It does not hold together: a functional spec a job, every req with the first.
518
571
  say(`the plan did not hold together (${checkDraft(input, draft)[0]?.slice(0, 120)}) - a functional spec a pulse`);
519
572
  placement = undefined;
520
- groups.splice(0, groups.length, ...numbered.map((spec, index) => ({ specs: [spec.number], title: titleOf(specs[index]) })));
573
+ groups.splice(0, groups.length, ...(await titled(numbered.map((spec, index) => ({ specs: [spec.number], title: titleOf(specs[index]) })))));
521
574
  chunks = chunksFor(groups);
522
575
  place();
523
576
  const simple = draftOf(chunks, net);
@@ -586,6 +639,22 @@ export function placePrompt(input) {
586
639
  "Call submit_placement once, with every one of them placed once.",
587
640
  ].join("\n");
588
641
  }
642
+ export function titlePrompt(groups, track) {
643
+ return [
644
+ track === "conformance"
645
+ ? "You title the pulses that test a codebase. Each group below is a pulse: it writes and runs the conformance tests for its functional specs - black-box tests of the code as built."
646
+ : "You title the pulses that build a codebase. Each group below is a pulse: a coding agent builds its functional specs into the code.",
647
+ "",
648
+ track === "conformance"
649
+ ? "Give each group a title that says what its pulse tests - the behaviour, not the spec's wording."
650
+ : "Give each group a title that says what its pulse builds - the feature, not the spec's wording.",
651
+ `At most ${TITLE_WORDS} words and ${TITLE_CHARS} characters: it heads the pulse where the person watches the work, and a longer one is cut short there.`,
652
+ "",
653
+ ...groups.map((group) => [`<group number="${group.number}">`, ...group.specs.map((spec) => `<functional spec>\n${spec.trim()}\n</functional spec>`), "</group>"].join("\n")),
654
+ "",
655
+ "Call submit_titles once, with a title for every group, by its number.",
656
+ ].join("\n");
657
+ }
589
658
  const TargetSchema = Type.Union([Type.Number(), Type.Literal("all"), Type.Literal("built")]);
590
659
  const NOTES = "Each of the person's wishes for how the queue is planned that you did not follow, and why - a limit it would break, say. Leave it out when you followed them all, or there are none.";
591
660
  /** Ask one step of an agent, and ask again with what was wrong, until it holds or the retries are spent. */
@@ -608,7 +677,7 @@ async function askStep(options) {
608
677
  session.dispose();
609
678
  }
610
679
  }
611
- /** The chunking's two agents, on a model - fresh for every plan. */
680
+ /** The chunking's agents, on a model - fresh for every plan. */
612
681
  export function llmChunkAgents(options) {
613
682
  const label = options.label ?? "queue planner";
614
683
  return {
@@ -654,5 +723,27 @@ export function llmChunkAgents(options) {
654
723
  },
655
724
  }),
656
725
  }),
726
+ title: (groups, check, track) => askStep({
727
+ cwd: options.cwd,
728
+ model: options.model,
729
+ label: `${label} titles`,
730
+ task: titlePrompt(groups, track),
731
+ check,
732
+ tool: (take) => defineTool({
733
+ name: "submit_titles",
734
+ label: "Submit the titles",
735
+ description: `A title for every group, by its number: at most ${TITLE_WORDS} words and ${TITLE_CHARS} characters each.`,
736
+ parameters: Type.Object({
737
+ titles: Type.Array(Type.Object({
738
+ group: Type.Number(),
739
+ title: Type.String({ description: `What the group's pulse ${track === "conformance" ? "tests" : "builds"}: at most ${TITLE_WORDS} words and ${TITLE_CHARS} characters.` }),
740
+ })),
741
+ }),
742
+ async execute(_id, params) {
743
+ take(params);
744
+ return { content: [{ type: "text", text: "Recorded." }], details: {}, terminate: true };
745
+ },
746
+ }),
747
+ }),
657
748
  };
658
749
  }
@@ -88,11 +88,11 @@ export type LedgerEvent = {
88
88
  why: string;
89
89
  } | {
90
90
  kind: "planner.failed";
91
- step: "group" | "place";
91
+ step: "group" | "place" | "title";
92
92
  error: string;
93
93
  } | {
94
94
  kind: "planner.fellBack";
95
- step: "group" | "place";
95
+ step: "group" | "place" | "title";
96
96
  why: string;
97
97
  } | {
98
98
  kind: "model.call";
@@ -174,7 +174,9 @@ A job that renders the whole of a new spec is too big to build well, and a rende
174
174
  2. **The definitions each group uses**, in code: the concepts its functional specs name, and the concepts those definitions name in turn. A definition goes with the first group that uses it; later groups have it built. One nothing uses goes with the last.
175
175
  3. **Where each new implementation req goes.** An agent places each with the group it is about - or with the first, when it is about everything (the language, the framework).
176
176
 
177
- Changes to what is built - an item edited or taken out, a functional spec built worded differently - go first, in a job of their own. Every answer is checked: every new functional spec once, in order, next to each other, at most `max-specs-per-pulse`; every req placed once. One that does not hold goes back to its agent, twice at most, and then the plan is a functional spec a job with every req in the first. A group whose functional specs and the definitions they bring pass `max-lines-per-pulse` (120 unset) is split into as few pulses as fit - its functional specs kept together in runs, in order, as long as each run stays within it, `Comments (1 of 2)`, `Comments (2 of 2)` - not a functional spec a job; its reqs do not count, as a split leaves them with its first job whatever happens. The planner runs on the chat's model in a conversation, and on the harness model under `turri regenerate`. No agent reviews its plan: the harness checks it - every change in exactly one job, the spec holding together after each, the last ending on the spec as it is.
177
+ Each pulse's title says what it does - at most 10 words and 70 characters, as it heads the pulse where the person watches the work. The grouping agent titles its groups; groups no agent made - the conformance tests', and a grouping that fell back - are titled by a third agent, told what each pulse does: what it builds, or, for the conformance tests, what they test. Without it, or when it fails, a pulse is titled by its functional specs' first lines, cut short.
178
+
179
+ Changes to what is built - an item edited or taken out, a functional spec built worded differently - go first, in a job of their own. Every answer is checked: every new functional spec once, in order, next to each other, at most `max-specs-per-pulse`; every req placed once; every group titled once, within the limit. One that does not hold goes back to its agent, twice at most, and then the plan is a functional spec a job with every req in the first. A group whose functional specs and the definitions they bring pass `max-lines-per-pulse` (120 unset) is split into as few pulses as fit - its functional specs kept together in runs, in order, as long as each run stays within it, `Comments (1 of 2)`, `Comments (2 of 2)` - not a functional spec a job; its reqs do not count, as a split leaves them with its first job whatever happens. The planner runs on the chat's model in a conversation, and on the harness model under `turri regenerate`. No agent reviews its plan: the harness checks it - every change in exactly one job, the spec holding together after each, the last ending on the spec as it is.
178
180
 
179
181
  Each planned job knows its cases - which functional specs it adds, changes (their text, or a concept they use) and takes out, or none: reqs and definitions only - and the loop says them, with each job's title, where the person looks (`drift`, `pulses`, and what `regenerate` returns).
180
182
 
@@ -312,7 +314,7 @@ They follow the code: a functional spec gets conformance tests once its code is
312
314
 
313
315
  ### Conformance tests in the work loop
314
316
 
315
- In the work loop, conformance is a track beside the code's. What the conformance tests are written for is recorded apart - `tests/.turri.json`: the functional specs they test, as built, with their acceptance tests, the definitions and implementation reqs they were written against, and the test reqs they follow - and the work is from that record to what they are to be: of the functional specs the code is built from, those chosen - every one, or those `conform` names - as the spec has them, with its test reqs. A functional spec whose code is behind the spec - built from other text, or not in the code record at all, as while `regenerate` regenerates it again by name - keeps its tests until the code lands; they are taken out only once the spec and the code have both dropped it. The harness cuts the work into jobs, and no agent is asked: every functional spec tested - added or changed, asked for by `conform`, chosen, or left by the code jobs - is in one conformance job, a module's own together, as far as a guardrail lets: a new job begins only where the next functional spec would take the job's functional specs and the definitions they bring past 120 lines. The reqs they are written against go with the first job, and do not count. When the person asks for it - one at a time, separately - `conform`'s `one_by_one` (`turri conform --one-by-one`) makes it a job a functional spec instead, until conformance tests are asked for again without it. And when the person asks for them in pulses of their own grouping - 1-3 and 4-6 - `conform`'s `groups` gives each group a job of its own: a job never takes functional specs of two groups, and within a group the guardrail still cuts. Only work that touches no functional spec at all, the test reqs changed say, is a job without one. A module's conformance jobs run on its lane once its code is built - every code job of it landed - so the module is tested once, as built: one explorer, one run of the whole suite, rather than a conformance cycle after each code job; and they pass before anything is built on it. Until then they are not planned, but what is planned lists them last - `conformance tests - planned once the code is built` - wherever they are on for the whole module, so asking for them is seen to have worked; `conform` asked again for a module they are on for already says so, and changes nothing.
317
+ In the work loop, conformance is a track beside the code's. What the conformance tests are written for is recorded apart - `tests/.turri.json`: the functional specs they test, as built, with their acceptance tests, the definitions and implementation reqs they were written against, and the test reqs they follow - and the work is from that record to what they are to be: of the functional specs the code is built from, those chosen - every one, or those `conform` names - as the spec has them, with its test reqs. A functional spec whose code is behind the spec - built from other text, or not in the code record at all, as while `regenerate` regenerates it again by name - keeps its tests until the code lands; they are taken out only once the spec and the code have both dropped it. The harness cuts the work into jobs - an agent only titles them: every functional spec tested - added or changed, asked for by `conform`, chosen, or left by the code jobs - is in one conformance job, a module's own together, as far as a guardrail lets: a new job begins only where the next functional spec would take the job's functional specs and the definitions they bring past 120 lines. The reqs they are written against go with the first job, and do not count. When the person asks for it - one at a time, separately - `conform`'s `one_by_one` (`turri conform --one-by-one`) makes it a job a functional spec instead, until conformance tests are asked for again without it. And when the person asks for them in pulses of their own grouping - 1-3 and 4-6 - `conform`'s `groups` gives each group a job of its own: a job never takes functional specs of two groups, and within a group the guardrail still cuts. Only work that touches no functional spec at all, the test reqs changed say, is a job without one. A module's conformance jobs run on its lane once its code is built - every code job of it landed - so the module is tested once, as built: one explorer, one run of the whole suite, rather than a conformance cycle after each code job; and they pass before anything is built on it. Until then they are not planned, but what is planned lists them last - `conformance tests - planned once the code is built` - wherever they are on for the whole module, so asking for them is seen to have worked; `conform` asked again for a module they are on for already says so, and changes nothing.
316
318
 
317
319
  A conformance job is a spec diff's conformance run: the tests of its change written, changed or taken out, a failure judged - the test or the code wrong - and fixed, then the whole suite with the unit tests, the tests the module inherits with them. The run that passed already was of the whole suite - the module's tests folder holds the chain's, copied up - so the suite is run once more only when the code or the tests changed after it passed with the unit tests; otherwise it is not run again. Passing, the tests record is written and committed with the tests; otherwise all of it - code fixes included - is rolled back, and the module halts until the spec or the code changes, or `conform` asks again: its conformance tests, its code - nothing more is built on code whose conformance tests did not pass - and every module built on it, which waits.
318
320
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turri-dev/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {