Files
msd-core/.changeset
0xdhx 7249bdddac fix(#4294): reserve a full progress bar for 100% and give the render half one owner (#4473)
* fix(#4294): reserve a full progress bar for 100% and give the render half one owner

Six call sites each carried `Math.round((percent / 100) * width)` inline, and
every copy rounded to a full bar before the percent reached 100: from 95 up at
width 10, from 98 up at width 20. A project at 19/20 plans drew the same bar as
a shipped one beside a number that said otherwise, and an out-of-range percent
threw `RangeError` from the unguarded `'░'.repeat`.

ADR-3180 Decision 7 gave the completion-RATIO derivation one owner
(`clampPercentFromFraction`). This gives the RENDER half the same:
`progressBarFilledCells` / `renderProgressBar` in phase-lifecycle.cts, with the
`progress` table and bar renderers, the stats renderer, the gsd2 import writer,
and #4231's `formatProgressMachineSegment` (which now serves both STATE.md
writers) all drawing through it.

Contract: below 100 the fill is held one cell short of the width, so only the
saturating percents move (95-99 at width 10, 98-99 at width 20) and every other
value in 0-100 renders as before — pinned by an exhaustive comparison against
the legacy formula at both widths. Null / non-finite renders an empty bar;
out-of-range is clamped, never thrown.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012hbFn24VWaxJBw8DUWjAmU

* chore(#4294): set changeset fragment pr to 4473

* docs(#4294): correct the pre-fix inline call-site count to five

The kernel's doc comment said SIX call sites carried their own
`Math.round((percent / 100) * width)`. The base tree has five: three in
`commands.cts` plus one each in `gsd2-import.cts` and
`formatProgressMachineSegment`, the latter two using `/ 10` with the width
already substituted (`pct` and `clamped` respectively).

The six is #4294's count of consumers -- it counts `cmdStateUpdateProgress`
and `syncCore` separately, but #4231 had already routed both through
`formatProgressMachineSegment` (as it does `applyPostSyncPreservation`), so by
this branch's base they share one copy. The comment now states the tree's count
and records where the six comes from, so neither number reads as an error
later.

Comment-only; no behaviour change, and no change to compiled output.

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-09-11 00:42:55 -04:00
..

Changeset Fragments

This directory holds per-PR CHANGELOG fragments. Every PR with user-facing changes drops one (or more) <random-name>.md files here describing its CHANGELOG entry. Fragments are consolidated into the top-level CHANGELOG.md at release time.

Why

Two PRs that both edit the ### Fixed block of CHANGELOG.md always conflict on merge — git can't pick a serialization order without human input. Two PRs that each add a fresh .changeset/<unique-name>.md never conflict because they don't share lines.

See #2975 for the full rationale.

Adding a fragment

node scripts/changeset/new.cjs \
  --type Fixed \
  --pr 1234 \
  --body "fix the thing — explain the user-visible change in one sentence"

This writes .changeset/<adjective>-<noun>-<noun>.md with frontmatter and a body. Three random words → concurrent PRs don't collide.

Format

---
type: Fixed
pr: 1234
---
**`/gsd-foo` no longer drops trailing slashes** — explain the user-visible change.

Allowed type: values follow Keep a Changelog: Added, Changed, Deprecated, Removed, Fixed, Security.

Opting out

PRs that legitimately have no user-facing impact can add the no-changelog label. CI honors it. When unsure, add the fragment.

At release time

Promotion is automatic. The release workflow's finalize job runs:

node scripts/changeset/cli.cjs render --version vX.Y.Z --date YYYY-MM-DD --allow-empty

This reads every fragment, groups bullets by type:, replaces ## [Unreleased] with a new ## [vX.Y.Z] - YYYY-MM-DD block, opens a fresh ## [Unreleased] above, and deletes consumed fragments. The --allow-empty flag ensures a no-change release still gets a dated heading (with a _No notable changes._ placeholder). A subsequent verify step confirms the promotion landed correctly. Maintainers do not run this by hand.

Archived fragments

.changeset/archived/ holds fragments for already-shipped releases (≤ 1.3.1), retained for provenance. Their content was hand-curated into the dated ## [1.x.y] sections of CHANGELOG.md during the #690 backfill — they were never consumed by render. All changeset tooling enumerates .changeset/ non-recursively, so archived fragments are never picked up or rendered. Do not move them back to the top level.