@forwardimpact/libwiki 0.3.0 → 0.3.1

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 (49) hide show
  1. package/README.md +30 -29
  2. package/package.json +1 -1
  3. package/src/active-claims.js +5 -5
  4. package/src/agent-roster.js +2 -2
  5. package/src/audit/admission.js +14 -11
  6. package/src/audit/conflict-markers-rule.js +9 -9
  7. package/src/audit/grammar.js +21 -18
  8. package/src/audit/rule-builders.js +20 -19
  9. package/src/audit/rules.js +30 -27
  10. package/src/audit/scopes.js +34 -33
  11. package/src/audit/status-row.js +14 -15
  12. package/src/block-renderer.js +5 -4
  13. package/src/boot.js +10 -8
  14. package/src/budget-gate.js +39 -36
  15. package/src/budget.js +3 -3
  16. package/src/cli-definition.js +19 -16
  17. package/src/commands/audit.js +3 -3
  18. package/src/commands/boot.js +1 -1
  19. package/src/commands/claim.js +44 -38
  20. package/src/commands/curate.js +34 -31
  21. package/src/commands/fix.js +68 -64
  22. package/src/commands/inbox.js +1 -1
  23. package/src/commands/init.js +12 -7
  24. package/src/commands/ledger.js +11 -11
  25. package/src/commands/log.js +19 -17
  26. package/src/commands/memo.js +4 -1
  27. package/src/commands/product-mix.js +16 -15
  28. package/src/commands/refresh.js +25 -22
  29. package/src/commands/rotate.js +9 -8
  30. package/src/commands/sync.js +26 -17
  31. package/src/conflict-markers.js +21 -21
  32. package/src/constants.js +37 -33
  33. package/src/gitattributes.js +10 -9
  34. package/src/integrity.js +29 -27
  35. package/src/issue-list-renderer.js +24 -16
  36. package/src/lane-files.js +11 -10
  37. package/src/ledger/anchor.js +6 -6
  38. package/src/ledger/projection.js +35 -32
  39. package/src/ledger/reader.js +4 -4
  40. package/src/marker-scanner.js +3 -2
  41. package/src/sanitize.js +12 -11
  42. package/src/secret-gate.js +41 -40
  43. package/src/status.js +12 -11
  44. package/src/storyboard-skeleton.js +20 -18
  45. package/src/util/agent-flag.js +8 -8
  46. package/src/util/clock.js +1 -1
  47. package/src/util/wiki-dir.js +7 -7
  48. package/src/weekly-log.js +115 -101
  49. package/src/wiki-sync.js +393 -361
package/src/weekly-log.js CHANGED
@@ -8,12 +8,13 @@ import {
8
8
  WEEKLY_LOG_WORD_BUDGET,
9
9
  } from "./constants.js";
10
10
 
11
- // Block seam inside a day-section: a `### ` heading at line start. The finer
12
- // grain rotation falls to when a lone day-section alone exceeds a budget.
11
+ // Block seam inside a day-section: a `### ` heading at line start. Rotation
12
+ // falls to this finer grain when a lone day-section alone exceeds a budget.
13
13
  const BLOCK_SEAM_RE = /^### /;
14
14
 
15
- // ISO week computation lives in libutil's calendar util (the one place a
16
- // `new Date` is allowed); re-exported here for the existing public surface.
15
+ // libutil's calendar util computes the ISO week. It is the one place that may
16
+ // call `new Date`. This module re-exports the function for the existing
17
+ // public surface.
17
18
  export { isoWeek } from "@forwardimpact/libutil";
18
19
 
19
20
  /** Return the path of the current weekly log file for an agent. */
@@ -27,10 +28,10 @@ function partPathAt(filePath, n) {
27
28
  return path.join(dir, `${base}-part${n}.md`);
28
29
  }
29
30
 
30
- // Find `count` part slots that are each verified free, skipping any occupied
31
- // `-partN.md` (e.g. a numbering gap left by a manually deleted middle part).
32
- // Every returned slot is unoccupied, so the seal never overwrites a pre-existing
33
- // part on commit nor unlinks one on rollback.
31
+ // Find `count` part slots that are each verified free. Skip any occupied
32
+ // `-partN.md` (e.g. a gap in the numbers after someone deleted a middle part
33
+ // by hand). Every returned slot is unoccupied, so the seal never overwrites a
34
+ // pre-existing part on commit nor unlinks one on rollback.
34
35
  function nextFreeSlots(filePath, count, fs) {
35
36
  const slots = [];
36
37
  let n = 1;
@@ -61,9 +62,9 @@ function residueOf(sec, partIndex, measure) {
61
62
  }
62
63
 
63
64
  /**
64
- * An over-cap prologue cannot merge with any day-section (adding content only
65
- * grows it), so it always seals as its own part 0. Flag it up front as the
66
- * first residue so it is never shipped silently over budget.
65
+ * An over-cap prologue cannot merge with any day-section, because more content
66
+ * only grows it. So it always seals as its own part 0. Flag it up front as the
67
+ * first residue so it never ships silently over budget.
67
68
  */
68
69
  function prologueResidue(prologue, { overBudget, measure }) {
69
70
  if (prologue.length === 0 || !overBudget(prologue)) return null;
@@ -72,23 +73,25 @@ function prologueResidue(prologue, { overBudget, measure }) {
72
73
  }
73
74
 
74
75
  /**
75
- * Greedily pack day-sections into part bodies under both budgets, the prologue
76
- * riding with part 1. A chunk that alone exceeds a budget — a lone day-section
77
- * or an over-cap prologue — is sealed as its own part and recorded as the
78
- * (first) residue; packed runs and single sections are kept under both budgets,
79
- * so the only over-cap part bodies are the ones `residue` accounts for.
76
+ * Greedily pack day-sections into part bodies under both budgets. The prologue
77
+ * rides with part 1. A chunk that alone exceeds a budget seals as its own part,
78
+ * and the packer records it as the (first) residue. Such a chunk is a lone
79
+ * day-section or an over-cap prologue. Packed runs and single sections stay
80
+ * under both budgets, so the only over-cap part bodies are the ones `residue`
81
+ * accounts for.
80
82
  * @param {Array<{date: string, text: string}>} sections
81
- * @param {string} prologue - Content above the first seam; rides with part 1.
83
+ * @param {string} prologue - Content above the first seam. It rides with
84
+ * part 1.
82
85
  * @param {{overBudget: (s: string) => boolean, measure: (s: string) => {lines: number, words: number}}} budget
83
86
  * @returns {{partBodies: string[], residue: null | {section: string, lines: number, words: number, partIndex: number}}}
84
87
  */
85
88
  function packSections(sections, prologue, budget) {
86
89
  const { overBudget, measure } = budget;
87
90
  const partBodies = [];
88
- // The prologue, when over budget, is always pushed first (part 0) — record it
89
- // before packing so a later lone-section residue cannot displace it.
91
+ // The packer always pushes an over-budget prologue first (part 0). Record it
92
+ // before you pack, so a later lone-section residue cannot displace it.
90
93
  let residue = prologueResidue(prologue, budget);
91
- let open = prologue; // body of the part currently being filled
94
+ let open = prologue; // body of the part the packer fills now
92
95
  let opened = prologue.length > 0;
93
96
  const flush = () => {
94
97
  if (opened) {
@@ -99,7 +102,7 @@ function packSections(sections, prologue, budget) {
99
102
  };
100
103
  for (const sec of sections) {
101
104
  if (overBudget(sec.text)) {
102
- // Irreducible lone section: flush the open part, then seal it alone.
105
+ // Irreducible lone section. Flush the open part. Then seal it alone.
103
106
  flush();
104
107
  residue ??= residueOf(sec, partBodies.length, measure);
105
108
  partBodies.push(sec.text);
@@ -118,10 +121,10 @@ function packSections(sections, prologue, budget) {
118
121
  }
119
122
 
120
123
  /**
121
- * Slice `body` at `seamRe` seam offsets into `{label, text}` sections, the
122
- * label being the seam's full matched heading line (date or `### ` heading).
124
+ * Slice `body` at `seamRe` seam offsets into `{label, text}` sections. Each
125
+ * label is the seam's full matched heading line (date or `### ` heading).
123
126
  * Returns `{prologue, sections}` where the prologue is everything above the
124
- * first seam (the whole body when there are no seams). Concatenating the
127
+ * first seam (the whole body when there are no seams). A concatenation of the
125
128
  * prologue and every section's text reproduces `body` byte-for-byte.
126
129
  */
127
130
  function findSections(body, seamRe) {
@@ -131,7 +134,7 @@ function findSections(body, seamRe) {
131
134
  while ((match = re.exec(body)) !== null) {
132
135
  const eol = body.indexOf("\n", match.index);
133
136
  const headingLine = body.slice(match.index, eol === -1 ? body.length : eol);
134
- // A captured group (the day seam's date) labels the section; otherwise the
137
+ // A captured group (the day seam's date) labels the section. Otherwise the
135
138
  // full heading line (a `### ` block heading) is the label.
136
139
  const label = match[1] ?? headingLine;
137
140
  seams.push({ offset: match.index, label });
@@ -150,23 +153,27 @@ function findSections(body, seamRe) {
150
153
 
151
154
  /**
152
155
  * Split an over-budget weekly-log source at its `## YYYY-MM-DD` day-section
153
- * seams into an ordered list of conforming parts. Pure — no I/O.
156
+ * seams into an ordered list of conforming parts. The function is pure. It does
157
+ * no I/O.
154
158
  *
155
- * The first line of `text` is the original H1; it is consumed and replaced by
156
- * per-part H1s, never appearing in a part body. Everything after that first
157
- * line is the body, sliced at the day-section seam byte offsets so that
158
- * concatenating the parts' bodies reproduces the original body byte-for-byte.
159
- * The prologue (any content above the first seam) rides with part 1. Sections
160
- * are greedily packed left-to-right under both the line- and word-budget, with
161
- * each candidate part measured H1-included so its own H1 is charged.
159
+ * The first line of `text` is the original H1. The function consumes it and
160
+ * replaces it with per-part H1s, so it never appears in a part body. Everything
161
+ * after that first line is the body. The function slices the body at the
162
+ * day-section seam byte offsets, so a concatenation of the parts' bodies
163
+ * reproduces the original body byte-for-byte. The prologue (any content above
164
+ * the first seam) rides with part 1. The function packs sections greedily from
165
+ * left to right under both the line- and word-budget. It measures each
166
+ * candidate part with the H1 included, so the measurement charges the part's
167
+ * own H1.
162
168
  *
163
- * When a lone day-section alone exceeds a budget, it is re-bisected at its
164
- * `### ` block seams (one grain finer) and the resulting block-parts replace it,
165
- * so a single over-cap day no longer forces a hand-split. Only a single `### `
166
- * block that alone exceeds a budget — or an over-cap seamless prologue — remains
167
- * an irreducible residue: it is sealed as its own (over-budget) part and named
168
- * in `residue` (the residue's `section` then names the block heading, not a
169
- * date); the rest still packs normally.
169
+ * When a lone day-section alone exceeds a budget, the function re-bisects it at
170
+ * its `### ` block seams (one grain finer). The block-parts then replace it, so
171
+ * a single over-cap day no longer forces a hand-split. Only these remain an
172
+ * irreducible residue: a single `### ` block that alone exceeds a budget, or an
173
+ * over-cap seamless prologue. The function seals such a chunk as its own
174
+ * (over-budget) part and names it in `residue`. The residue's `section` then
175
+ * names the block heading. It does not name a date. The rest still packs
176
+ * normally.
170
177
  *
171
178
  * @param {string} text - The full weekly-log source (H1 + body).
172
179
  * @param {string} agent - Agent profile id (e.g. "staff-engineer").
@@ -177,9 +184,9 @@ export function bisectWeeklyLog(text, agent, isoWeekStr) {
177
184
  const nl = text.indexOf("\n");
178
185
  const body = nl === -1 ? "" : text.slice(nl + 1);
179
186
  const title = agentTitle(agent);
180
- // `(part N of M)` costs the same 1 line and 4 word-tokens regardless of the
181
- // digits in N/M, so a fixed template measures every part exactly without
182
- // needing to know M before packing finishes.
187
+ // `(part N of M)` costs the same 1 line and 4 word-tokens whatever the
188
+ // digits in N/M are. So a fixed template measures every part exactly. It
189
+ // does not need M before the packer finishes.
183
190
  const h1Template = `# ${title} — ${isoWeekStr} (part 1 of 1)`;
184
191
  const measure = (chunk) => {
185
192
  const rendered = `${h1Template}\n${chunk}`;
@@ -199,12 +206,12 @@ export function bisectWeeklyLog(text, agent, isoWeekStr) {
199
206
  };
200
207
 
201
208
  const budget = { overBudget, measure };
202
- // Locate the day-section seams (date at line-start, trailing suffix
203
- // tolerated, e.g. `## 2026-05-19 (third activation)`).
209
+ // Locate the day-section seams. The date sits at line-start. The regex
210
+ // tolerates a trailing suffix, e.g. `## 2026-05-19 (third activation)`.
204
211
  const { prologue, sections } = findSections(body, WEEKLY_LOG_SEAM_RE);
205
212
 
206
- // Zero day-sections: the whole body is the prologue and its own single part,
207
- // flagged as a residue when it alone exceeds a budget.
213
+ // Zero day-sections. The whole body is the prologue and its own single part.
214
+ // The function flags it as a residue when it alone exceeds a budget.
208
215
  if (sections.length === 0) {
209
216
  return finish([body], prologueResidue(body, budget));
210
217
  }
@@ -212,7 +219,7 @@ export function bisectWeeklyLog(text, agent, isoWeekStr) {
212
219
  const { partBodies, residue } = packSections(sections, prologue, budget);
213
220
  // A lone day-section that alone exceeds a budget is the only residue
214
221
  // `packSections` can produce here (the prologue rides part 1). Re-bisect that
215
- // day at its `### ` block seams and splice the block-parts in for it; the
222
+ // day at its `### ` block seams and splice the block-parts in for it. The
216
223
  // surfaced residue becomes the inner one (an over-cap block) or null.
217
224
  if (residue !== null) {
218
225
  const sub = resplitDaySection(partBodies, residue, budget);
@@ -223,22 +230,24 @@ export function bisectWeeklyLog(text, agent, isoWeekStr) {
223
230
 
224
231
  /**
225
232
  * Re-bisect the lone over-cap day-section that `packSections` flagged as the
226
- * residue: split that part's body at its `### ` block seams and splice the
227
- * resulting block-bodies into `partBodies` in its place. The day-section's body
228
- * carries its own `## ` heading as a prologue above the first `### ` block, so
229
- * that heading rides the first block-part. Returns the spliced `partBodies` and
230
- * the inner residue (a single over-cap `### ` block, or null when every block
231
- * now conforms), re-indexed to its position in the spliced list.
233
+ * residue. Split that part's body at its `### ` block seams. Then splice the
234
+ * block-bodies into `partBodies` in its place. The day-section's body carries
235
+ * its own `## ` heading as a prologue above the first `### ` block, so that
236
+ * heading rides the first block-part. Returns the spliced `partBodies` and the
237
+ * inner residue (a single over-cap `### ` block, or null when every block now
238
+ * conforms). The function re-indexes that residue to its position in the
239
+ * spliced list.
232
240
  */
233
241
  function resplitDaySection(partBodies, residue, budget) {
234
242
  const dayBody = partBodies[residue.partIndex];
235
- // Only a day-section (its body opens with a `## ` heading) is re-split at the
236
- // block grain; an over-cap seamless prologue has no day to descend into and
237
- // stays the terminal residue.
243
+ // The function re-splits only a day-section (its body opens with a `## `
244
+ // heading) at the block grain. An over-cap seamless prologue has no day to
245
+ // descend into and stays the terminal residue.
238
246
  if (!dayBody.startsWith("## ")) return { partBodies, residue };
239
247
  const { prologue, sections } = findSections(dayBody, BLOCK_SEAM_RE);
240
- // No `### ` block seam inside the day: nothing finer to cut — keep the day as
241
- // the irreducible residue (criterion 3's terminal case at the day grain).
248
+ // No `### ` block seam inside the day. Nothing finer is left to cut. Keep the
249
+ // day as the irreducible residue (criterion 3's terminal case at the day
250
+ // grain).
242
251
  if (sections.length === 0) return { partBodies, residue };
243
252
  const { partBodies: blockBodies, residue: blockResidue } = packSections(
244
253
  sections,
@@ -261,13 +270,14 @@ function resplitDaySection(partBodies, residue, budget) {
261
270
  }
262
271
 
263
272
  /**
264
- * Stage every write to `${path}.tmp`, then commit by renaming each `leading`
265
- * write onto its path (tracked for rollback) and the `anchor` write LAST — the
266
- * single point of no return. The anchor is the live/source file: until its
267
- * rename it still holds its original bytes, so a failure anywhere unlinks every
268
- * committed leading path and remaining temp and re-throws, leaving the anchor's
269
- * path/contents/inode untouched. Leading paths must be verified-free slots (a
270
- * rollback unlinks them). Returns the leading paths, in order.
273
+ * Stage every write to `${path}.tmp`. Then commit. Rename each `leading` write
274
+ * onto its path (tracked for rollback). Rename the `anchor` write LAST. That
275
+ * last rename is the single point of no return. The anchor is the live/source
276
+ * file. Until its rename it still holds its original bytes. So a failure
277
+ * anywhere unlinks every committed leading path and remaining temp, and
278
+ * re-throws. The anchor's path, contents, and inode stay untouched. Leading
279
+ * paths must be verified-free slots (a rollback unlinks them). Returns the
280
+ * leading paths, in order.
271
281
  *
272
282
  * @param {Array<{path: string, content: string}>} leading - Committed first.
273
283
  * @param {{path: string, content: string}} anchor - Committed last.
@@ -305,9 +315,9 @@ function commitAtomic(leading, anchor, fs) {
305
315
  }
306
316
 
307
317
  /**
308
- * Seal a bisected weekly log: write each part to a fresh `-partN.md` slot and a
309
- * fresh empty main over `filePath` (the anchor, committed last). Returns the
310
- * `-partN.md` slot paths in part order.
318
+ * Seal a bisected weekly log. Write each part to a fresh `-partN.md` slot.
319
+ * Write a fresh empty main over `filePath` (the anchor, committed last).
320
+ * Returns the `-partN.md` slot paths in part order.
311
321
  *
312
322
  * @param {string} filePath - The current weekly-log path.
313
323
  * @param {Array<{h1: string, body: string}>} parts - Ordered parts to seal.
@@ -327,25 +337,26 @@ function atomicSeal(filePath, parts, agent, isoWeekStr, fs) {
327
337
  }
328
338
 
329
339
  /**
330
- * Rotate the current weekly log, sealing an over-budget source into
331
- * budget-conforming parts via a bisecting seal. Returns a tagged union:
340
+ * Rotate the current weekly log. The function bisects an over-budget source and
341
+ * seals it into budget-conforming parts. Returns a tagged union:
332
342
  * `{status:"noop"}` (no rotation needed), `{status:"sealed",parts}` (sealed
333
343
  * into one-or-more conforming parts), or `{status:"incomplete",parts,residue}`
334
- * (a lone day-section exceeds a budget and is named).
344
+ * (a lone day-section exceeds a budget, and the result names it).
335
345
  *
336
- * A `noop` return carries a `reason` — `"missing"` (no file; no size measured),
337
- * `"floor"` (header-only/empty body; nothing to seal), or `"under-budget"`
338
- * (under both budgets without `--force`) — plus the measured `lines`/`words`
339
- * for the two reasons that read the file, so the CLI guard need not re-read it.
340
- * "Over budget" is decided here over *either* budget (lines or words), so a
341
- * caller never needs `force: true` to seal a word-over/line-under log.
346
+ * A `noop` return carries a `reason`. The reason is `"missing"` (no file, no
347
+ * size measured), `"floor"` (header-only or empty body, nothing to seal), or
348
+ * `"under-budget"` (under both budgets without `--force`). The return also
349
+ * carries the measured `lines`/`words` for the two reasons that read the file,
350
+ * so the CLI guard need not re-read it. This function decides "over budget"
351
+ * over *either* budget (lines or words), so a caller never needs `force: true`
352
+ * to seal a word-over/line-under log.
342
353
  *
343
354
  * @returns {{status: "noop"|"sealed"|"incomplete", reason?: "missing"|"floor"|"under-budget", lines?: number, words?: number, fromPath: string, parts?: string[], residue?: {path: string, section: string, lines: number, words: number}}}
344
355
  * @param {string} wikiRoot
345
356
  * @param {string} agent
346
357
  * @param {string} today - ISO date string.
347
358
  * @param {{lines?: number, words?: number}} [delta={}] - Projected post-append
348
- * line/word delta; the trigger fires when the current file plus this delta
359
+ * line/word delta. The trigger fires when the current file plus this delta
349
360
  * would breach either budget. Force-rotate callers pass `{}`.
350
361
  * @param {{force?: boolean}} [options]
351
362
  * @param {object} fs - Sync filesystem surface (`runtime.fsSync`).
@@ -366,10 +377,11 @@ export function rotateIfOverBudget(
366
377
  const text = fs.readFileSync(filePath, "utf-8");
367
378
  const lines = countLines(text);
368
379
  const words = countWords(text);
369
- // A header-only (or empty) log has nothing to seal. Without this floor,
370
- // force-rotating a freshly-reset main would mint an empty `(part 1 of 1)`
371
- // file and reset the main again — once per invocation, forever. The floor
372
- // holds even under `--force`, so it is checked before the force branch.
380
+ // A header-only (or empty) log has nothing to seal. Without this floor, a
381
+ // force-rotate of a freshly-reset main would mint an empty `(part 1 of 1)`
382
+ // file and reset the main again. That would repeat once per invocation,
383
+ // forever. The floor holds even under `--force`, so the code checks it
384
+ // before the force branch.
373
385
  const nl = text.indexOf("\n");
374
386
  if ((nl === -1 ? "" : text.slice(nl + 1)).trim() === "") {
375
387
  return {
@@ -380,11 +392,11 @@ export function rotateIfOverBudget(
380
392
  fromPath: filePath,
381
393
  };
382
394
  }
383
- // Over either budget (lines or words), decided once here in core: a
395
+ // Core decides "over either budget" (lines or words) once, here. So a
384
396
  // word-over/line-under log seals without `--force`. The projection folds in
385
397
  // the caller's append delta so a pre-append rotate fires on the post-append
386
398
  // size. The `noop`/`under-budget` arm carries the measured size so the CLI
387
- // handler can report the resolved target without re-reading the file.
399
+ // handler can report the resolved target and need not re-read the file.
388
400
  const { lines: dLines = 0, words: dWords = 0 } = delta;
389
401
  const projectedLines = lines + dLines;
390
402
  const projectedWords = words + dWords;
@@ -422,11 +434,11 @@ export function rotateIfOverBudget(
422
434
 
423
435
  /**
424
436
  * Derive the agent, ISO week, and MAIN-log path from a sealed part's path. The
425
- * week comes from the filename (a part may belong to a past week, not today),
426
- * and the main-log path — not the part path — is what `nextFreeSlots` must base
427
- * new sibling slots on. Returns null for a non-conforming filename. Shares
428
- * WEEKLY_LOG_PART_NAME_RE with the audit's file classifier so the two cannot
429
- * drift on the filename convention.
437
+ * week comes from the filename, because a part may belong to a past week
438
+ * instead of today. `nextFreeSlots` must base new sibling slots on the main-log
439
+ * path. It must not use the part path. Returns null for a non-conforming
440
+ * filename. Shares WEEKLY_LOG_PART_NAME_RE with the audit's file classifier so
441
+ * the two cannot drift on the filename convention.
430
442
  */
431
443
  function parsePartPath(partPath) {
432
444
  const m = path.basename(partPath).match(WEEKLY_LOG_PART_NAME_RE);
@@ -441,8 +453,8 @@ function parsePartPath(partPath) {
441
453
  }
442
454
 
443
455
  /**
444
- * Reseal a re-bisected part: the first sub-part overwrites the source slot (the
445
- * anchor, committed last) and the rest claim fresh sibling slots of the main-log
456
+ * Reseal a re-bisected part. The first sub-part overwrites the source slot (the
457
+ * anchor, committed last). The rest claim fresh sibling slots of the main-log
446
458
  * path. `nextFreeSlots` skips occupied slots (including the source's own), so a
447
459
  * commit never clobbers a sibling nor a rollback unlinks a pre-existing one.
448
460
  * Returns `[partPath, ...newSlots]` in part order.
@@ -463,16 +475,18 @@ function atomicResealPart(partPath, mainLogPath, parts, fs) {
463
475
 
464
476
  /**
465
477
  * Re-bisect a single over-budget sealed weekly-log PART in place. Agent and ISO
466
- * week come from the part filename. A part within both budgets is a noop; a part
467
- * whose body cannot be reduced (a lone over-cap day-section or an over-cap
468
- * zero-seam body) is left BYTE-IDENTICAL and reported `incomplete` with a
469
- * residue, so the re-audit re-flags it for a human. Otherwise the first sub-part
470
- * overwrites `partPath` (slot reused) and the remaining sub-parts land on fresh
471
- * sibling slots, with full rollback (source untouched on any failure).
478
+ * week come from the part filename. A part within both budgets is a noop. A
479
+ * part whose body cannot be reduced stays BYTE-IDENTICAL. The function reports
480
+ * it `incomplete` with a residue, so the re-audit re-flags it for a human. Such
481
+ * a part is a lone over-cap day-section or an over-cap zero-seam body.
482
+ * Otherwise the first sub-part overwrites `partPath` (slot reused) and the
483
+ * remaining sub-parts land on fresh sibling slots, with full rollback (source
484
+ * untouched on any failure).
472
485
  *
473
486
  * The produced sub-parts carry `bisectWeeklyLog`'s `(part i of M)` H1s, where M
474
- * is LOCAL to this part's split — not a global count of the week's parts.
475
- * Sibling parts are never renumbered (the audit does not validate the numbers).
487
+ * is LOCAL to this part's split. M is not a global count of the week's parts.
488
+ * The function never renumbers sibling parts (the audit does not validate the
489
+ * numbers).
476
490
  *
477
491
  * @param {string} partPath - Absolute path to an `<agent>-YYYY-Www-partN.md`.
478
492
  * @param {object} fs - Sync filesystem surface (`runtime.fsSync`).
@@ -493,7 +507,7 @@ export function rebisectOverBudgetPart(partPath, fs) {
493
507
  parsed.agent,
494
508
  parsed.isoWeekStr,
495
509
  );
496
- // A single produced part has no splittable seam: leave the file untouched and
510
+ // A single produced part has no splittable seam. Leave the file untouched and
497
511
  // surface a residue (synthesised from the file when the bisector did not name
498
512
  // one) so the caller's re-audit re-flags it.
499
513
  if (parts.length === 1) {