halfcycle 0.4.10 → 0.4.12

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/index.js CHANGED
@@ -2082,11 +2082,16 @@ var phaseReviewViewSchema = z13.discriminatedUnion("state", [
2082
2082
 
2083
2083
  // ../events/dist/build-record-v3.js
2084
2084
  var BUILD_RECORD_V3_FORMAT = "halfcycle-build-record/v3";
2085
+ var recordRoundStatedSchema = z14.object({
2086
+ actor: attestedCloseSchema.shape.actor,
2087
+ statements: attestedStatementsSchema
2088
+ }).strict();
2085
2089
  var recordReviewRoundSchema = z14.object({
2086
2090
  round: z14.number().int().min(1),
2087
2091
  openedAt: utcIso8601,
2088
2092
  verdict: walkVerdictSchema,
2089
- findings: z14.number().int().min(0)
2093
+ findings: z14.number().int().min(0),
2094
+ stated: recordRoundStatedSchema.optional()
2090
2095
  }).strict();
2091
2096
  var recordReviewApprovalSchema = z14.discriminatedUnion("kind", [
2092
2097
  z14.object({
@@ -2148,6 +2153,9 @@ var recordReviewSchema = z14.object({
2148
2153
  if (r.round !== i + 1) {
2149
2154
  ctx.addIssue({ code: "custom", path: ["rounds", i, "round"], message: "rounds must be numbered 1, 2, 3 \u2026 in order with no gap" });
2150
2155
  }
2156
+ if (i === v.rounds.length - 1 && r.stated !== void 0) {
2157
+ ctx.addIssue({ code: "custom", path: ["rounds", i, "stated"], message: "the last round's words are the close's own, so the round does not repeat them" });
2158
+ }
2151
2159
  });
2152
2160
  const seen = /* @__PURE__ */ new Set();
2153
2161
  v.parts.forEach((p, i) => {
@@ -5226,12 +5234,13 @@ function machines(coverage, checks) {
5226
5234
  const line = `${label}${String(coverage.enrolled)}. Of these, ${String(coverage.conformant)} had reported Halfcycle's checks as fully set up.`;
5227
5235
  return checks === "not-observed" && coverage.conformant > 0 ? `${line} Set up is not the same as running: Halfcycle has no record of any check running during this phase.` : line;
5228
5236
  }
5229
- function walkLines(walk, p) {
5237
+ function walkLines(walk, p, closingRound = false) {
5230
5238
  const label = "> - Acceptance walk (the finished work tried as a user would, by someone other than its builder): ";
5231
5239
  if (walk === null)
5232
5240
  return [`${label}the person closing the phase reported none.`];
5241
+ const it = closingRound ? "the round that closed the phase" : "it";
5233
5242
  const lines = [
5234
- `${label}${walk.verdict === "clean" ? "the person closing the phase said it found no problems." : "the person closing the phase said it found problems."}`
5243
+ `${label}${walk.verdict === "clean" ? `the person closing the phase said ${it} found no problems.` : `the person closing the phase said ${it} found problems.`}`
5235
5244
  ];
5236
5245
  walk.findings.forEach((finding, i) => {
5237
5246
  lines.push(`> - Finding ${String(i + 1)}: ${p.text(finding)}`);
@@ -5250,12 +5259,12 @@ function decisionLine(attested) {
5250
5259
  }
5251
5260
  return attested.walk !== null && attested.walk.verdict === "defects" ? "> - Decision: close the phase as it stood, with the problems its acceptance walk found." : "> - Decision: the phase's agreed finish conditions were met.";
5252
5261
  }
5253
- function renderStated(attested, p) {
5262
+ function renderStated(attested, p, closingRound = false) {
5254
5263
  const lines = [
5255
5264
  decisionLine(attested),
5256
5265
  `> - Name given: ${attested.actor === null ? "removed with the account that made this close" : p.text(attested.actor)}`,
5257
5266
  `> - Reason given: ${attested.reason === null ? "none." : p.text(attested.reason)}`,
5258
- ...walkLines(attested.walk, p),
5267
+ ...walkLines(attested.walk, p, closingRound),
5259
5268
  `> - Code closed at (commit): ${statedOrNot(attested.closeCommit, p)}`,
5260
5269
  `> - What the phase set out to prove: ${statedOrNot(attested.statements.thesis, p)}`,
5261
5270
  `> - What came out of it: ${statedOrNot(attested.statements.outcome, p)}`
@@ -5271,7 +5280,7 @@ function renderStated(attested, p) {
5271
5280
  }
5272
5281
  return lines.join("\n");
5273
5282
  }
5274
- function renderClose(heading, observed, attested, checks, p) {
5283
+ function renderClose(heading, observed, attested, checks, p, closingRound = false) {
5275
5284
  return [
5276
5285
  heading,
5277
5286
  headline(observed, attested, checks),
@@ -5283,7 +5292,7 @@ function renderClose(heading, observed, attested, checks, p) {
5283
5292
  machines(observed.seatCoverage, checks)
5284
5293
  ].join("\n"),
5285
5294
  STATED_LABEL,
5286
- renderStated(attested, p)
5295
+ renderStated(attested, p, closingRound)
5287
5296
  ];
5288
5297
  }
5289
5298
  var REVIEW_LABEL = "**Recorded by Halfcycle from the owner's review.** Each part the review held, and how the project's owner decided it. A line marked as the agent's is the agent's own answer, not Halfcycle's.";
@@ -5332,6 +5341,37 @@ function renderReview(record2, p) {
5332
5341
  blocks.push(removals);
5333
5342
  return blocks;
5334
5343
  }
5344
+ var EARLIER_ROUNDS_LABEL = `${STATED_LABEL} What was said each time the owner's review was asked for, before the round that closed the phase. What was said for that last round is under the close above. If the account that asked for a round is later deleted, the name given for that round is removed.`;
5345
+ function renderRoundStated(round, stated, p) {
5346
+ const label = "> - Acceptance walk (the finished work tried as a user would, by someone other than its builder): ";
5347
+ const walk = round.verdict === "clean" ? "the person asking for this round said it found no problems." : round.findings === 0 ? "the person asking for this round said it found problems." : `the person asking for this round said it found ${counted(round.findings, "problem", "problems")}.`;
5348
+ const lines = [
5349
+ `> - Name given: ${stated.actor === null ? "removed with the account that asked for this round" : p.text(stated.actor)}`,
5350
+ `${label}${walk}`,
5351
+ `> - What the phase set out to prove: ${statedOrNot(stated.statements.thesis, p)}`,
5352
+ `> - What came out of it: ${statedOrNot(stated.statements.outcome, p)}`
5353
+ ];
5354
+ const deviations = stated.statements.deviations;
5355
+ if (deviations.length === 0) {
5356
+ lines.push("> - Changes from the plan: None stated by the client.");
5357
+ } else {
5358
+ lines.push("> - Changes from the plan:");
5359
+ deviations.forEach((deviation, i) => {
5360
+ lines.push(`> - Change ${String(i + 1)}: ${p.text(deviation)}`);
5361
+ });
5362
+ }
5363
+ return lines.join("\n");
5364
+ }
5365
+ function renderEarlierRounds(record2, p) {
5366
+ const rounds = record2.review.rounds;
5367
+ const blocks = [];
5368
+ for (const round of rounds) {
5369
+ if (round.stated === void 0)
5370
+ continue;
5371
+ blocks.push(`### Round ${String(round.round)} of ${String(rounds.length)}`, renderRoundStated(round, round.stated, p));
5372
+ }
5373
+ return blocks.length === 0 ? [] : ["## Earlier rounds of the review", EARLIER_ROUNDS_LABEL, ...blocks];
5374
+ }
5335
5375
  function renderBuildRecordV2Markdown(record2) {
5336
5376
  const p = makePrinter();
5337
5377
  const attestedBySequence = /* @__PURE__ */ new Map();
@@ -5345,6 +5385,8 @@ function renderBuildRecordV2Markdown(record2) {
5345
5385
  const closes = [...record2.observed.closes].sort((a, b) => a.sequence - b.sequence);
5346
5386
  const total = closes.length;
5347
5387
  const latest = closes[total - 1];
5388
+ const rounds = record2.format === "halfcycle-build-record/v3" ? record2.review.rounds : [];
5389
+ const roundsAfter = rounds.length <= 1 ? null : `after ${String(rounds.length)} rounds of the owner's review.${rounds.some((r) => r.stated !== void 0) ? ' What was said for each earlier round is under "Earlier rounds of the review".' : ""}`;
5348
5390
  const body = [
5349
5391
  `Phase: ${phase} (the name the client gave it)`,
5350
5392
  OBSERVED_LABEL,
@@ -5353,12 +5395,12 @@ function renderBuildRecordV2Markdown(record2) {
5353
5395
  `- Project identifier: ${p.text(record2.observed.engagementId)}. Halfcycle keeps this project's records under this identifier.`,
5354
5396
  `- Record format: ${p.text(record2.format)}. This names the layout this file and the .json file beside it follow, for software that reads them.`
5355
5397
  ].join("\n"),
5356
- total === 1 ? "This phase was closed once." : `This phase was closed ${String(total)} times. The latest close is described first; each earlier close follows, as it was recorded when it was made.`,
5398
+ total === 1 ? roundsAfter === null ? "This phase was closed once." : `This phase was closed once, ${roundsAfter}` : `This phase was closed ${String(total)} times. The latest close is described first; each earlier close follows, as it was recorded when it was made.${roundsAfter === null ? "" : ` The latest close came ${roundsAfter}`}`,
5357
5399
  ...renderChecks(record2.observed.checks, p),
5358
- ...renderClose(total === 1 ? "## The close" : `## The latest close (close ${String(latest.sequence)} of ${String(total)})`, latest, attestedBySequence.get(latest.sequence), record2.observed.checks.state, p)
5400
+ ...renderClose(total === 1 ? "## The close" : `## The latest close (close ${String(latest.sequence)} of ${String(total)})`, latest, attestedBySequence.get(latest.sequence), record2.observed.checks.state, p, rounds.length > 1)
5359
5401
  ];
5360
5402
  if (record2.format === "halfcycle-build-record/v3")
5361
- body.push(...renderReview(record2, p));
5403
+ body.push(...renderReview(record2, p), ...renderEarlierRounds(record2, p));
5362
5404
  if (total > 1) {
5363
5405
  body.push("## Earlier closes");
5364
5406
  for (const close of closes.slice(0, -1)) {