Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
44a6ed4
SCALE-SEAM (95) — element state: two read/write pairs, and why the ma…
claude Sep 4, 2026
121c095
SCALE-SEAM (96) — the as-built question's aggregate reader, and a wit…
claude Sep 4, 2026
2f09a5c
Merge already-squashed (95) branch history
claude Sep 4, 2026
0472f97
SCALE-SEAM (97) — the undo stack, and a destination that looked right…
claude Sep 4, 2026
66ab67a
Merge already-squashed (96) branch history
claude Sep 4, 2026
58525e1
Correct the undo/redo republish docs — all three sites, not just the …
claude Sep 4, 2026
75f8597
SCALE-SEAM (98) — detailing carriers, and a field map total over one …
claude Sep 4, 2026
f2926a5
Merge already-squashed (97) branch history
claude Sep 4, 2026
83e149e
SCALE-SEAM (99) — the content shelf, and a destination header that wa…
claude Sep 4, 2026
17103db
Merge already-squashed (98) branch history
claude Sep 4, 2026
2a8f46f
SCALE-SEAM (100) — the element-connection pair, and a destination nam…
claude Sep 4, 2026
6d68219
Merge already-squashed (99) branch history
claude Sep 4, 2026
370a1f8
Merge already-squashed (95)-(100) branch history
claude Sep 4, 2026
f5d6751
R22-ENTITLEMENT ⑤ — an agency review comment becomes an RFI somebody …
claude Sep 4, 2026
5c79d56
Merge branch history — reconcile after the (95)-(100) squashes
claude Sep 4, 2026
d826115
R22-ENTITLEMENT ⑤ review: promotion claims the comment atomically, an…
claude Sep 4, 2026
9f048eb
Merge branch history after the #434 squash — content already identical
claude Sep 4, 2026
c437a7f
R24-REPORTS-BY-MOMENT — a finished pack can be sent, not only downloaded
claude Sep 4, 2026
87cc0e8
Review round on #435: five findings, all verified real, all fixed
claude Sep 4, 2026
67dfc36
Second review round on #435: the port fix is the root cause my first …
claude Sep 4, 2026
eff9568
Merge after the #435 squash
claude Sep 4, 2026
0fadd1c
Only committed capital owns anything — cap table and waterfall
claude Sep 4, 2026
87f3a59
Merge after the #436 squash
claude Sep 4, 2026
2fa0692
SCALE-SEAM (101) — design-phase predicted performance, client.ts 642 …
claude Sep 4, 2026
276c4e5
Merge after the #437 squash
claude Sep 4, 2026
ea414ab
Portfolio risk heat map — R22-PIPELINE
claude Sep 4, 2026
9ba106e
Review round on #439 — deterministic tie-break, keyboard-operable rows
claude Sep 4, 2026
4ae3e28
Merge remote-tracking branch 'origin/main' into claude/codebase-audit…
claude Sep 4, 2026
27d6f40
Cross-project Gantt — R22-PIPELINE
claude Sep 4, 2026
3f07a03
Review round on #440 — reject normalised dates, require the id separa…
claude Sep 4, 2026
285f262
Merge remote-tracking branch 'origin/main' into claude/codebase-audit…
claude Sep 4, 2026
09a2446
Portfolio resourcing — R22-PIPELINE's last item
claude Sep 4, 2026
b614e0b
Merge remote-tracking branch 'origin/main' into claude/codebase-audit…
claude Sep 4, 2026
fadc11d
Roadmap truth pass + R39-DECOMP-VIEWER (17)
claude Sep 5, 2026
8c9314d
SCALE-SEAM (102) — counterparty risk, client.ts 603 -> 589
claude Sep 5, 2026
c7a58ae
Review fixes on #442, and a gate for the number that keeps drifting
claude Sep 5, 2026
a2b33eb
Merge remote-tracking branch 'origin/main' into claude/codebase-audit…
claude Sep 5, 2026
e1f1a05
SCALE-SEAM (103) — acceptance gates, client.ts 589 -> 572
claude Sep 5, 2026
9875dba
Merge remote-tracking branch 'origin/main' into claude/codebase-audit…
claude Sep 5, 2026
7e2f4fd
SCALE-SEAM (104) — coverage maps, client.ts 572 -> 554
claude Sep 5, 2026
9ac2ca7
Merge remote-tracking branch 'origin/main' into claude/codebase-audit…
claude Sep 5, 2026
236d33d
The app.ts figure was wrong in four places; the gate reached one
claude Sep 5, 2026
5ba8bf8
Fix an unmatched ** I introduced while writing about drift
claude Sep 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@ All notable changes to Massing. Releases are signed, auto-updating desktop build
(Windows / macOS / Linux); the updater always serves the latest. Format loosely follows
[Keep a Changelog](https://keepachangelog.com/).

## Unreleased — the app.ts figure was wrong in four places, and the gate that was supposed to stop that reached one of them

**Corrects `docs/roadmap.md`, and widens `services/api/test_claude_md_gates.py` to the class rather
than the instance.**

The R39-DECOMP-VIEWER entry carried **three different figures for one file**: a header saying
`app.ts` is "5,160 → **3,311**, a 36% cut", a shipped block saying "5,160 → **2,944**, a 43% cut",
and the ratchet pinning **2,508**. A fourth sat elsewhere in the file — "5,064 → 3,444 → and thirteen
slices since". The header also claimed "seven slices shipped" directly above a list naming eleven.

**The two baselines were both real, and neither was labelled.** `CLAUDE.md` measures from 5,064 —
the file before the first extraction commit of 2026-08-06 — while the roadmap measured from 5,160, a
**same-day peak** partway through it: the file grew before it shrank. Nothing on the page could tell
a reader the two were counted from different points. The baseline is now stated, matches `CLAUDE.md`,
and the current figure is the ratchet's own count.

**The gate is the real subject here.** The previous release added a check binding `CLAUDE.md`'s
narrative `app.ts` figure to the `test_file_sizes.py` pin, on the reasoning that *prose is a copy of
a gated value and a copy is what drifts*. That reasoning was right and the implementation was too
narrow: it gated **one copy of a number that lives in four places**, and the other three were already
wrong when it shipped. *Gating the instance you happened to be looking at is not gating the class.*
The check now scans every doc that carries the baseline-anchored form.

**It immediately earned that.** Having corrected the two figures I found by reading, the widened
check failed on a **third** I had missed entirely.

**And mutation testing found a defect in the widened check itself.** Its first draft used a summed
floor — "at least 2 figures across the docs" — and reported "3 figures across 2 docs" while **all
three came from the roadmap**: `CLAUDE.md` words it "5,064 lines to", so the arrow pattern never
matched it and its contribution was zero. A mutation that reformatted only the roadmap took the
count to 0 and exposed it. That is precisely the per-doc vacuity failure this same file's citation
ratchet was built to prevent, repeated one screen further down. The floor is now **per-doc**, and
`CLAUDE.md` is covered by name rather than by assumption.

## Unreleased — SCALE-SEAM (104): coverage maps

Extracts `spineTraceability` and `scopeRegister` from `client.ts` (**572 → 554**) into
Expand Down
18 changes: 14 additions & 4 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,7 @@ four phantom entries — one of them a bare `SCALE-SEAM` with its `㉘` silently
exact failure `roadmapLanes.test.ts` documents in its `MARKS` note. The gates caught all four.)*

* **Not R39-DECOMP-VIEWER ③.** It ranked second last time on a size-ceiling argument that had
already gone false. `app.ts` is decomposing steadily (5,064 → 3,444 → and thirteen slices since), the ratchet
already gone false. `app.ts` is decomposing steadily (5,064 → 2,508, slices still landing), the ratchet
is pinned, and it moves on its own whenever a feature pushes it. It does not need a sprint; it
needs to keep being interleaved. **Re-measure the ceiling before ever promoting it again** — that
is the specific error row 2 made.
Expand Down Expand Up @@ -2546,8 +2546,10 @@ re-open): the converter build stage moved to the supported Node LTS with a pinne
refusal (`services/api/src/aec_api/main.py`), and full-history checkout for the secret-scan job.


- 🟡 **R39-DECOMP-VIEWER ③** *(L, Lane E — **seven slices shipped; `app.ts` is 5,160 → 3,311, a 36%
cut. The `builders` map is entirely gone.** The paragraph below saying the extraction "is NOT begun"
- 🟡 **R39-DECOMP-VIEWER ③** *(L, Lane E — **`app.ts` is 5,064 → 2,508, a 50% cut. The `builders`
map is entirely gone.** Slice count deliberately not restated here: `services/api/test_file_sizes.py`
is the record, and a count repeated in prose is a copy that drifts — this header said "seven slices"
while the list immediately below it named eleven. The paragraph below saying the extraction "is NOT begun"
was true on 2026-08-06 and stayed on the page until 2026-08-17, through six shipped slices — a
roadmap entry describing work as un-started while the work is being done is worse than a missing
entry, because it sends the next reader to re-derive a plan that was already executed. Kept only
Expand All @@ -2559,10 +2561,18 @@ refusal (`services/api/src/aec_api/main.py`), and full-history checkout for the
⑤ project-browser panel (216) · ⑥ `loadProjectModel` (37) · ⑦ **drawings & sheets (142, v0.3.978)** ·
⑨ **fabrication detail (65)** · ⑩ **MEP / fire / life safety (169)** — both v0.3.981 ·
⑫ **envelope & free-form geometry (75, v0.3.982)** · ⑬ **model federation & version compare
(88, v0.3.1043)**. `app.ts` 5,160 → **2,944**, a **43% cut**.
(88, v0.3.1043)**. `app.ts` 5,064 → **2,508**, a **50% cut**.
Each ratcheted `services/api/test_file_sizes.py` down, never reset. `services/api/test_file_sizes.py`
carries the per-slice history; that comment, not this list, is the record.

*(**Three figures for one file, two of them stale — corrected 2026-09-05.** This block said
`5,160 → 2,944` and the header said `5,160 → 3,311`, while the ratchet pinned **2,508**. The two
BASELINES also disagreed with `CLAUDE.md`, which says 5,064, and both were real: 5,064 is the file
before the first extraction commit of 2026-08-06, and **5,160 was a same-day peak** partway through
it — the file grew before it shrank. Neither number was ever labelled, so nothing could tell a
reader they were measuring from different points. Baseline is now stated and matches `CLAUDE.md`;
the current figure is the ratchet's own count, which is the only one under a gate.)*

**"never reset" was false for two days, and the way it was found is the transferable part.** Slices
⑭⑮⑯ walked the pin 2_865 → 2_757 → 2_630 → 2_571 on 2026-08-27, and a REL-4 portal commit to the
same shared file later that afternoon carried the pre-⑭ line back in — value *and* comment trail, so
Expand Down
47 changes: 47 additions & 0 deletions services/api/test_claude_md_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,53 @@ def resolve(name):
# Both halves must be FOUND, not just agree. If either pattern stops matching — the sentence is
# reworded, the ratchet key is renamed — this check would otherwise pass on two Nones, which is the
# vacuous-green failure this file's own header calls worse than no gate.
# The SAME figure lives in `docs/roadmap.md` twice, and the gate above did not reach it. Added
# 2026-09-05, one PR after that gate: the roadmap's R39-DECOMP-VIEWER entry carried THREE figures for
# `app.ts` — a header saying "5,160 -> 3,311", a shipped block saying "5,160 -> 2,944", and the truth,
# 2,508. It also used a DIFFERENT baseline from CLAUDE.md (5,160 vs 5,064) with neither labelled, so
# nothing could tell a reader they were measured from different points; both were real, 5,064 being the
# file before the first extraction commit and 5,160 a same-day peak partway through it.
#
# The lesson is about the gate, not the numbers. The check above was written to stop a narrative copy of
# a pinned value from drifting, and it gated ONE copy of a number that lives in three places. **Gating
# the instance you happened to be looking at is not gating the class.** So this scans every doc for the
# baseline-anchored form and requires each to equal the pin — which also holds the BASELINE steady,
# since a figure measured from somewhere else no longer matches the pattern at all.
#
# Historical mentions are deliberately still matchable only by their own baseline: the corrected text
# quotes "5,160 -> 2,944" as the thing that was wrong, and that does not match this pattern, so the
# record of the error survives without failing the build.
APP_TS_BASELINE = "5,064"
_arrow = re.compile(re.escape(APP_TS_BASELINE) + r"\s*(?:->|\u2192)\s*\*{0,2}([\d,]+)")

#: Docs carrying the ARROW form, and the minimum each must contribute. PER-DOC, never summed — the
#: first draft of this check used a summed floor of 2 and reported "3 figures across 2 docs" while
#: **all three came from the roadmap**: CLAUDE.md words it "5,064 lines to", so the arrow pattern
#: never matched it and its contribution was ZERO. A mutation that reformatted only the roadmap took
#: the count to 0 and revealed it. That is the exact failure this file's own citation ratchet was
#: built to avoid, repeated one screen further down — **a summed floor lets one doc hold the number
#: up while another silently contributes nothing.** CLAUDE.md is not listed here because the two
#: checks below cover its wording directly; a doc is either in this map or covered by name, never
#: assumed.
ARROW_DOCS = {"docs/roadmap.md": 3}

_bad, _thin = [], []
for _doc, _min in ARROW_DOCS.items():
with open(os.path.join(ROOT, _doc), encoding="utf-8") as fh:
_hits = _arrow.findall(fh.read())
if len(_hits) < _min:
_thin.append(f"{_doc}={len(_hits)} (min {_min})")
for _h in _hits:
if _pinned and int(_h.replace(",", "")) != int(_pinned.group(1).replace("_", "")):
_bad.append(f"{_doc}: {APP_TS_BASELINE} -> {_h}")

check(
"every doc that quotes the app.ts figure quotes the pinned one",
not _bad and not _thin,
(f"below the floor: {', '.join(_thin)}; " if _thin else "")
+ (f"DISAGREE: {', '.join(_bad)}" if _bad else "all figures match the pin"),
)

check(
"CLAUDE.md's app.ts sentence and the ratchet pin are both readable",
bool(_stated) and bool(_pinned),
Expand Down