Skip to content

feat(lib-007): the shelf is published, and a publish that worked stops reporting failure - #46

Merged
richardosborne14 merged 18 commits into
mainfrom
cline-dev
Sep 12, 2026
Merged

richardosborne14 merged 18 commits into
mainfrom
cline-dev

Conversation

@richardosborne14

Copy link
Copy Markdown

Closes LIB-007. The six parts v0.2.3 named and nobody could install are on the live shelf; this PR is the workflow defect that publishing them exposed, plus the record.

What is already true, before this merges

The publish ran (run 34648841604) and landed nodegx-content cf873c1e3. Asserted against the served index — not this repo, not the gate's own summary:

before after
served prefabs index 42 46
served modules index 30 32
verify-origin --require-published exit 1, six unreachable exit 0
Library check (LIB-001) in CI red for days green (see #45)

Advanced Columns, Format Date, Format Full Name, Sanitise Email, Charts, Media Recorder — payloads and icons all 200. Authenticated by a write deploy key on nodegx-content and nothing else; GITHUB_TOKEN cannot write cross-repo, which was the whole reason the step had been manual. No local checkout of the content repo anywhere in the loop (AC3).

The fix: a publish that worked stopped reporting failure

gh pr create for the baseline refresh was refused outright — "GitHub Actions is not permitted to create or approve pull requests" (can_approve_pull_request_reviews: false here). The branch was pushed, so nothing was lost, but a publish that had succeeded, been proved against the served index and left nothing to redo reported RED. That is the worst signal this workflow could emit, and it is precisely the rot §3 of the task is about — a red that means "everything worked" teaches people to stop reading reds.

The step now degrades: a ::warning:: with a ready-made compare URL and a job summary, instead of failing.

⚠️ Degrading is safe here only because the staleness it leaves is itself gated. library:verify-origin on the next PR or push exits 1 with a STALE BASELINE line per entry and prints the exact edit, so the unopened PR has a second owner. The comment says so, and says not to reuse the pattern where nothing else is watching.

Flipping the repo setting would make it automatic and is deliberately not a prerequisite: the same toggle also lets a workflow approve pull requests, which is a wider grant than publishing the shelf needs. That call is Richard's, not a side effect of this task.

Two things the build notes had wrong, corrected rather than dropped

The merge was never blocked. The notes deferred PR #44 to Richard because main has enforce_admins. It does — but the required checks are only Typecheck, Lint, Test (editor), Test (platform-node), Build and Check build artefacts, and all six passed; required reviews is 0; the branch was up to date, satisfying strict. The two reds are not required and were already red on main, one of them being LIB-007 itself. mergeStateStatus: UNSTABLE had been read as "blocked"; it means "mergeable, non-required checks red". Re-measuring that is what closed the task.

library:build is not byte-reproducible. The notes predicted "12 new files and the two index.json. Nothing else." The 12 are exactly right — the six parts and their icons. The commit touched 86 files: 74 already-published zips were rewritten. Their content is identical, verified by extracting old and new and diffing the trees rather than trusting the matching byte sizes; they differ at byte 11, the ZIP last-modified field. The earlier local diff -rq could not have seen this — it compared a fresh build against a fresh build.

No version bump is owed (library/README.md forbids changing content under an unchanged version, and the content did not change). What is owed is a warning to whoever builds the payload-hash check verify-origin still lacks: a naive hash would call all 74 drifted on every run. It is recorded in the baseline's $comment, where that work will trip over it, not only in a notes file.

AC5

The "searching the library for them today finds nothing" caveat and its ⚠️ bullet are deleted from the v0.2.3 release notes (gh release edit — those notes have no source file in this repo), verified by re-fetching the body. There is no second changelog artifact carrying it: git grep over all tracked files finds the sentence only in this phase's own documents, and CHANGELOG-COMMUNITY.md never made the claim. Measured, not assumed.

Not in scope, flagged

Lesson bundles (FIX-027) is red here and was already red on main before any of this. It is FIX-027's, not LIB-007's, and it is not a required check.

🤖 Generated with Claude Code

richardosborne14 and others added 18 commits September 11, 2026 23:29
…s reporting failure

The six parts v0.2.3 named and nobody could install are on the live origin:
prefabs 42 -> 46, modules 30 -> 32, published by CI from a deploy key with no
local checkout of nodegx-content anywhere in the loop (content repo cf873c1e3).
Asserted against the SERVED index, not this repo and not the gate's summary;
`library:verify-origin --require-published` exits 0.

The workflow still had a defect only running it could find. `gh pr create` for
the baseline refresh is refused outright when a repo has "Allow GitHub Actions
to create and approve pull requests" off, so a publish that had succeeded, been
proved and left nothing to redo reported RED. That is the worst signal this
workflow could emit and exactly the rot the task's section 3 is about. The step
now degrades to a warning carrying a ready-made compare URL. Safe only because
the staleness it leaves is itself gated - verify-origin exits 1 with a
STALE BASELINE line and the exact edit - so the unopened PR has a second owner.
The repo toggle would make it automatic and is deliberately not a prerequisite:
it also lets a workflow approve pull requests, which is wider than this needs.

Two things the notes had wrong are corrected rather than quietly dropped. The
merge was never blocked - enforce_admins is true but every REQUIRED check
passed, reviews are 0, and the branch was up to date; UNSTABLE had been read as
"blocked" when it means "mergeable, non-required checks red". And the blast
radius was understated: the publish rewrote 74 already-published zips whose
content is identical, differing at byte 11 - the ZIP mtime field. library:build
is not byte-reproducible, which the earlier diff -rq could not see because it
compared a fresh build against a fresh build. Recorded where the payload-hash
check that gate still lacks will trip over it.

AC5: the caveat is deleted from the v0.2.3 release notes. No second changelog
artifact carries it - measured, not assumed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The next-session prompt's entire FIRST JOB is done, so it is rewritten around
LIB-008 — the same defect one door along, and the one that is unassigned.

It carries the lesson this session actually cost: the previous handoff named
Richard for a merge that was never blocked. Required checks are a different set
from the check list, UNSTABLE means "mergeable, non-required checks red", and
the reds in question were already red on main. The instruction is now explicit -
ask the protection API which checks are required before inheriting a blocker.

Also registers three adjacent facts so nobody rediscovers them at full price:
the FIX-027 chain self-test red on main (2 of 12 mutations uncaught, not this
phase's), the still-uncommitted form-fields edit that owes a version bump, and
the repo's can_approve_pull_request_reviews being off.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The same session's blast-radius measurement turned out to be understated, so
the one AC that had only been read rather than seen was re-run: library:build
exits 0 (the build does not catch it) and library:verify-dist exits 1 naming
the entry. Restored, re-gated green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`opennoodl-docs` was renamed to `nodegx-content` on 2026-08-07. GitHub Pages
does not follow a repo-rename redirect the way git and the API do, so the old
origin is a hard 404, not something a fetch survives. `getContentEndpoint()`
was repointed at the time; `getDocsEndpoint()` was not, and shipped dead in
0.2.3 — 176 node pages, the property panel, the node picker and the MCP
settings help link all resolved to a site that no longer exists.

The docs are now this monorepo's own Docusaurus site, served under a
`/docs` route base beneath a `/NodeGX/` baseUrl. The suffix is load-bearing:
a repoint without it turns a 404 *site* into a 404 *path* and looks fixed.
`McpSettingsSection`'s own `docs/` prefix comes off for the same reason — it
would have asked for `…/docs/docs/…`.

`nodeDocs.ts` stops deriving its path from the catalog's `docs` field, which
is an absolute URL on the legacy host, and returns a site-relative path so the
origin stays the caller's decision.

AC3/AC4: `npm run docs:verify-origin` resolves known pages through the
editor's OWN endpoint functions — called, not copied, so a future repoint is
followed with no edit to the gate — and checks BOTH origins, because
`getContentEndpoint` was equally healthy right up until a rename. It reports
ORIGIN GONE, PATH MOVED and UNAVAILABLE as distinguishable verdicts. Wired
into pr.yml as its own job, since a red step hidden behind a green one earlier
in the same job has already cost this release three findings.

Verified: docs:verify-origin exit 0 against both live origins;
tests-unit/alpha-006/nodeDocs.test.ts 20/20.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A deploy served from a sub-path (`nodegx deploy --base-url /x/`) publishes that
base as `Noodl.Env['BaseUrl']`. `resolveMediaSource` calls the runtime's
`getAbsoluteUrl`, which reads a DIFFERENT name — `Noodl.baseUrl` — that nothing
in the deploy ever sets, so it falls back to '/' and a project-relative picture
resolves to the domain root.

`Image` and `Video` apply the Env value themselves for exactly this reason.
This port is the third channel and was missing it. Measured 2026-09-11 on
nodegx.io/templates/business-landing-page/: the `<img>` resolved under the
sub-path and loaded, while this port's picture resolved to the root and 404'd.

The fix cannot go in `resolveMediaSource` — Image would then prepend the base a
second time, because the already-based URL still starts with '/'. And the
`!url.startsWith(baseUrl)` guard is not belt-and-braces: without it a value that
already carries the base gains it twice. An author deploying to a sub-path can
reasonably type the full path into this port, and a wire can carry one.
`Image.tsx` has no such guard and doubles in that case — a defect this port
should not copy, and the arm that caught it failed on the first version of
this fix.

Verified: exp018-background-image-base-url.test.ts 9/9.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carries two fixes, both measured against the live product:

- LIB-008 — every in-editor documentation link was a hard 404 in 0.2.3.
  `getDocsEndpoint()` still named the pre-rename GitHub Pages site, which
  does not follow a rename redirect. 176 node pages, the property panel, the
  node picker and the MCP settings help link all pointed at nothing.
- EXP-018 — a Background Image on a sub-path deploy resolved to the domain
  root and 404'd, while the `<img>` beside it loaded.

The parts library is NOT part of this cut and does not need to be: the editor
fetches the shelf from the content CDN at runtime, and LIB-007's publish
workflow put all 46 prefabs and 32 modules there on 2026-09-11. Verified today
by driving a 0.2.3 editor: 46 and 32 cards rendered, every icon resolved, no
entry version-gated, and Advanced Columns — one of the six parts 0.2.3 shipped
that nobody could install — cloned into a project cleanly. The 0.2.3 release
note's caveat is obsolete rather than carried forward.

`package-lock.json` also catches up the 0.2.2 -> 0.2.3 sync that was never
committed with the last release.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tsfixme ratchet went red on the commit before this one: the exp018 arms
raised the `any` count in tests from 382 to 388, and that gate exists to stop
exactly that from landing quietly.

`definition` and `host` were `any` because `addBackgroundInputs` takes a
`ReactNodeDefinition`, which demands `name` and `getReactComponent` — neither
of which any port `set` reads. So the stub is now described by a local
`BackgroundPorts` type naming the two fields the arms actually rely on, and
cast once at the call boundary. That is narrower than what it replaces: `any`
on `host` was also hiding that `styles` is nullable, which is now an optional
chain rather than a latent throw.

Not the raised-baseline path: the types were knowable, so PLAT-003's escape
hatch did not apply.

Verified: `npm run tsfixme` reports `any/tests 382 = baseline`; exp018 9/9.
Root `npm run typecheck` does not reach this file — its program includes
`packages/*/src/**` only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Committing the 2026-09-11 session's TPL-005 build, which was complete, gated and
driven but never committed — its own handoff recorded it as owed. Nothing here is
this session's work except the one fix named below, and the task file's D43 row.

  templates/pixel-game/          the artefact a person unzips
  packages/noodl-mcp/tests/      tpl005Components / Theme / Template / the gate
  scripts/generate-pixel-template.ts   npm run template:pixel
  scripts/devtools/harness-paths.js    D42 — TOKENS_SRC repointed at the contract
                                       package, so the render harness stops
                                       emitting `0 shipped defaults`
  scripts/devtools/deploy-from-disk.entry.ts   the D44 measurement's entry point

Two changes here ARE this session's, both forced by TPL-006:

- D51 — `tpl005Components.ts`'s `contentSized()` had no return annotation, so
  `tsc` narrowed the spread to `{ sizeMode: string }` and the two `delete`s were
  errors. `typecheck:mcp` had been red since this file landed; jest transpiles
  with babel and never type-checks, so nothing said so. Fixed; the typecheck is
  clean.
- The D43 row in TPL-005's task file is rewritten. It asked TPL-006's render to
  settle it, and TPL-006 did: the mechanism D43 named does not exist, and what
  actually froze this board is D49 — a States node with `useTransitions` true,
  which is the DEFAULT, never publishes a colour. `Game/Cell` carried three
  colour values and nothing else, so every output it had was in the frozen class.

⬜ NOT claimed: TPL-005's AC7 (the demo page) is still blocked on D44/D48, its
AC8 is still Richard's, and the prediction that `plBoardStates`/`plBannerStates`
have dead colour outputs is a prediction — nobody has driven it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ct is one JSON array

Richard: "you can build anything with Claude Code today, fine, but can you go in
and edit it afterwards?" This is the answer, and the sentence it is graded on is:
a person ships a completely different game by editing one JSON array and never
opens the node graph once.

  templates/story-engine/   9 components, 88 nodes, 84 connections, ZERO
                            noodl_modules, no backend, 0 validator errors
  npm run template:story    the generator; the drift gate runs the same builder
  62/62 on tpl006Template.test.ts · typecheck:mcp clean

Four verbs and no fifth: `goto` moves, `gives` hands you something, `requires`
hides a choice until you carry it, an absent `choices` array is an ending.

AC1 and AC3 are driven in a real browser, 0 console errors:
- the `requires` choice is ABSENT BEFORE and PRESENT AFTER, both halves read off
  the same passage in the same session — present-after alone grades nothing
- the remix box opens holding the story that is playing; a pasted two-passage
  array plays immediately and round-trips; four malformed pastes give four
  readable messages and stay on /remix

AC6 is stronger than it asked: all 20 passage titles, texts and choice labels
occur in exactly ONE parameter, checked in both the raw and JSON-escaped
spelling (raw-only found 13 of 20). And §8 proves it by doing it — the template
is rebuilt with a different story and every component graph diffed: one
component differs, one parameter of one node inside it.

Three product defects found, all filed with measurements:
- D49 replaces D43. A `States` node with `useTransitions` true — the DEFAULT —
  publishes its string and boolean values on a state change and never publishes
  a colour or a number. Two arms and a six-sample time series. D43's claim ("a
  value into currentState never changes the state") is false; ten shipped
  prefabs do that wire. This template ships transitions off and the gate pins it.
- D50. `uncollapsible-multi-column` Arm B lacks Arm A's content-size exclusion,
  so a wrapped row of content-sized pills is warned about and the suggested fix
  would give every two-word tag a 300px column.
- D51. `typecheck:mcp` was red for a day — see the TPL-005 commit.

⬜ NOT claimed: AC7 (the demo page) is BLOCKED on the inherited D44/D48 pair and
this template is more exposed than TPL-005 — its engine is Expression, Set
Variable, String Format, States and For Each item ports, all minted in a setup()
guarded on the editor connection. AC8 is Richard's. The category slug
(`interactive-fiction`) is still unruled and now blocks the shelf for two
templates. The drive ran through render-from-disk, not from a zip on a second
machine.

DEFECTS-THE-TEMPLATES-FOUND.md carries D40-D48 from the previous session as well
as this session's D49-D51; they were already in the working tree and D49 only
reads beside D43.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…dicts two broken templates

The next session's first job is a two-word fix nobody has driven: `useTransitions:
false` on TPL-005's two States nodes and TPL-004's FilterPill. D49 predicts their
colour outputs are dead, and TPL-004's AC8 click-drive — the banner that has been
on top since 09-11 — is the same job by another name.

Also recorded, because both cost this session real time:
- `render-from-disk.js` builds its HTML ONCE at startup and serves that snapshot,
  so three rebuilds were driven against a stale server and produced three
  confident wrong readings. Restart it between arms; `goto` is not enough.
- `drive-page.js eval` takes a statement body, not an expression.

The suite is stated honestly: 1738/1745 with 5 suites red, none of which imports
anything from tpl005*/tpl006*, and two of which this session did NOT bisect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ase it is

Two defects, both measured against the installed 0.2.3 app rather than reasoned
about from source.

## Half the server was never shipped

`packages/noodl-mcp/build.mjs` emits four artifacts into `dist/`;
`extraResources` listed two. So `kit-extract.cjs` and `cloud-bundle.cjs`
existed in every checkout and in no installed app. Both are *spawned* — the
server resolves them with `path.resolve(__dirname, '<x>.cjs')`, and `__dirname`
in a packaged app is `Resources/noodl-mcp/`, a directory holding the server
bundle and a catalog.

Asked over the live bound server from /Applications/NodeGX.app, `get_project_info`
answered:

    "kits": { "modules": [], "unavailable": "The kit extractor bundle is not
     present in this installation, so this project's own node types could not be
     read. Run `npm run build` in packages/noodl-mcp" }

Every project the editor creates has a `noodl_modules/` directory — the starter
modules put Inter and Lucide there — so the cheap "no modules" exit never fired
and every project on every install hit it. An agent authoring against a project
with a kit installed could not see that kit's node types at all, and the remedy
the message offers is meaningless to someone who installed a .dmg. The cloud
bundler was dead the same way.

## The version it reported could not distinguish any two releases

`@noodl/mcp`'s `package.json` has read `0.1.0` since the package was created and
has never been bumped, so the handshake said `noodl-mcp 0.1.0` whether the bundle
came from 0.1.0 or 0.2.4 — and the packaged directory carries no `package.json`
to check against. "My MCP server is stuck on an old version" was therefore a
report nobody could confirm or refute, which is how this shipped for four
releases without being noticed.

`build.mjs` now defines `__NODEGX_APP_VERSION__` from noodl-editor's version, so
the server reports the release it shipped inside. Taken from the file somebody
already has to bump to cut a release, so the two cannot drift; the
`package.json` read stays as the fallback a source run (ts-jest, tsx) reports.

## The gate that should have caught it had a hole shaped like it

`hls008ExportReact.test.ts` already asserts the *catalog* ships beside the
bundle — the identical requirement, checked for one file by name. Two more files
acquired that requirement later and nothing noticed.

So `mcpPackagingCompleteness.test.ts` names no files. It reads the resolvers'
own source for the `path.resolve(__dirname, '…cjs')` shape and requires every
name it finds to be built AND shipped into the bundle's directory, so a fifth
spawned bundle is covered the day it is written. It carries the guard against
the vacuous pass its subject needs: an arm that fails if the scan finds nothing.

Verified: removing the `kit-extract.cjs` entry reddens exactly one arm and names
the file; restored, 4/4 green. `npm run build` emits all four; the rebuilt
bundle carries `version: "0.2.4"`. noodl-mcp typecheck clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e ratchet

The Lint gate on cline-dev went red on the three files the phase-78 commits
added, which blocks the v0.2.4 release merge. This is TPL-006's third of it.

All 34 came from one root cause: the gate read `built.project` untyped, so every
component and node access carried its own `any`. The types already existed and
are precise — `LegacyProject.components` is `LegacyComponent[]`, `graph.roots` is
`LegacyNode[]`, `graph.connections` is `LegacyConnection[]` — so `nodesOf`,
`connectionsOf` and a new `componentsOf` are typed from the exporter's own
declarations rather than cast at each of the twenty call sites.

The one thing that genuinely is not typed by the format is `LegacyNode.ports`,
which is `unknown[]` and honestly so — the legacy format carries several port
shapes. `AuthoredPort` names the one this template authors, in one place, rather
than asserting it at each of the three reads.

§8 lost its casts too and gained a check it was missing: both sides of the
one-parameter diff must be non-empty, or two components that were never found
would agree perfectly and the spec would pass on nothing.

62/62 still green, `typecheck:mcp` clean, and the `any` population is back at its
baseline. `tpl005Template.test.ts` and `deploy-from-disk.entry.ts` are the other
two thirds and a peer session is mid-flight on both — deliberately left alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…atchet

Both template gates tripped it on landing and turned the required Lint check red
on cline-dev, blocking the v0.2.4 release merge. `typecheck:mcp` and a green jest
run both pass with a file full of `any`; `npm run tsfixme` is the only thing that
does not, so it goes in the pre-commit list for a new gate.

Also records what is still open and who is on it: +8 TSFixme in
deploy-from-disk.entry.ts with a peer mid-flight (do not edit without checking —
this session collided with them twice), and the uncommitted form-fields prefab
that reddens cmp004Parts and belongs to neither of us.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ly for the runtime it cannot see

The tsfixme ratchet went red on cline-dev and blocked the v0.2.4 merge. Three
files grew: two test files and the headless deploy entry. This is the half of
that owed by TPL-005 and the deploy entry; `7d82b3b01` covered TPL-006.

`tpl005Template.test.ts` — 14 `any` removed, none of them replaced by a
suppression. Every one was navigating `built.project.components`, which is
already `LegacyComponent[]`: `name` is a `string`, `graph` is a `LegacyGraph`,
`roots` is `LegacyNode[]` and `connections` is `LegacyConnection[]`. The casts
were noise on top of types that already existed, so `nodesOf` and
`connectionsOf` now return `LegacyNode[]` and `LegacyConnection[]` and the four
`(c as any).name` reads are plain `c.name`.

`deploy-from-disk.entry.ts` — 4 of 12 `TSFixme` typed the same way:
`registerRuntimeDiscoveredPorts` takes a `ProjectModel`, the node map and its
walk take `NodeGraphNode`, and `(comp.graph as TSFixme).roots` is just
`comp.graph.roots` because `NodeGraphModel.roots` is already `NodeGraphNode[]`.

The remaining 8 are the baseline raise, and the reason the gate asks for:
they are all `@noodl/runtime` internals that no declaration describes —
`probe.editorConnection`, `probe.graphModel` (x2), the `sendDynamicPorts`
payload (x2 sites), and `gm.listenersWithRefs[k]`. The root tsconfig maps
`@noodl/runtime` to generated declarations that do not cover these members, so
a real type here would have to be invented rather than read. That is the
documented case for raising it: typed where the type was knowable, raised only
where it is not.

Measured, not assumed: `npm run tsfixme` now exits 0 ("Holding the line"), and
the baseline diff is exactly `src TSFixme 563 -> 571` plus this file's own
0 -> 8 — no other marker moved, so no peer's in-flight debt was swept into it.
`npm run typecheck:mcp` exits 0 with 0 errors, and its tsconfig includes
`tests/**/*.ts`, so it does cover the edited spec.

⚠️ Nothing typechecks `scripts/devtools/`: it is in no tsconfig's include, so
the four types added there are verified by reading the declarations they came
from, not by a compiler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… and my pathspec commit swept them

Corrects the record on 23c23e4 rather than rewriting it, because that commit
is already on the branch.

23c23e4's message says 8 `TSFixme` remain in `deploy-from-disk.entry.ts` and
justifies raising the baseline 563 -> 571 for them: `@noodl/runtime` internals
no declaration describes. That was true when I measured it and false by the time
I committed. A peer typed those 8 in the working tree between my edit and my
`git commit <pathspec>`, and a pathspec commit takes the working-tree state of
that path — so their work landed inside my commit, under my message, and the
diff reads 51 insertions where mine was five lines. The file now carries zero
markers; the one `TSFixme` left in it is the word inside a comment.

So the raise was never earned. Baseline back to 563, byFile entry back to 0,
and no headroom for debt that no longer exists. `npm run tsfixme` exits 0.

Two things I got wrong, recorded so the next person does not repeat them:
- A pathspec commit is not a safe way to commit "only my files" on a shared
  checkout. It scopes by PATH, not by authorship, and a live peer editing the
  same path is swept in silently.
- Re-measuring after committing is what caught it: the ratchet said "8 fewer
  markers than the baseline", which is only possible if the tree had moved
  under the measurement the message was written from.

Typing credit for those 8 is the peer's, not mine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… that closed it

The +8 TSFixme in deploy-from-disk.entry.ts are typed and npm run tsfixme exits
0 with the baseline back at 563 — no raise survives.

The row also now says what actually happened, because the handoff's own warning
("a PEER IS MID-FLIGHT ON IT ... found it already half-typed underneath,
twice") was two sessions in one file at once, and it ended in a pathspec commit
sweeping the other session's uncommitted work into itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@richardosborne14
richardosborne14 merged commit f02f82e into main Sep 12, 2026
19 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant