feat(lib-007): the shelf is published, and a publish that worked stops reporting failure - #46
Merged
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-contentcf873c1e3. Asserted against the served index — not this repo, not the gate's own summary:verify-origin --require-publishedLibrary check (LIB-001)in CIAdvanced Columns, Format Date, Format Full Name, Sanitise Email, Charts, Media Recorder — payloads and icons all
200. Authenticated by a write deploy key onnodegx-contentand nothing else;GITHUB_TOKENcannot 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 createfor the baseline refresh was refused outright — "GitHub Actions is not permitted to create or approve pull requests" (can_approve_pull_request_reviews: falsehere). 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.library:verify-originon the next PR or push exits 1 with aSTALE BASELINEline 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
mainhasenforce_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, satisfyingstrict. The two reds are not required and were already red onmain, one of them being LIB-007 itself.mergeStateStatus: UNSTABLEhad been read as "blocked"; it means "mergeable, non-required checks red". Re-measuring that is what closed the task.library:buildis not byte-reproducible. The notes predicted "12 new files and the twoindex.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 localdiff -rqcould not have seen this — it compared a fresh build against a fresh build.No version bump is owed (
library/README.mdforbids changing content under an unchanged version, and the content did not change). What is owed is a warning to whoever builds the payload-hash checkverify-originstill 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 grepover all tracked files finds the sentence only in this phase's own documents, andCHANGELOG-COMMUNITY.mdnever made the claim. Measured, not assumed.Not in scope, flagged
Lesson bundles (FIX-027)is red here and was already red onmainbefore any of this. It is FIX-027's, not LIB-007's, and it is not a required check.🤖 Generated with Claude Code