release: v10.0.0 — the training diary, ascent logging cut, and the dependency pass - #145
Merged
Merged
Conversation
chore: back-merge v9.0.0 into dev
Ascents are cut, not deferred. Nothing in the app trains on one: the plan generator takes no new inputs and `current_grade_id` is stored rather than derived, so an ascent log would have fed only two unbuilt display views. The feature rested on `Ascent`'s docstring opening "the emotional payload of the whole app" — a sentence both `server/sessions/__init__.py` and #79 were quoting, and which the register mis-attributed to CLAUDE.md, where the word "ascent" has never appeared. The three `ascent*` tables stay as inert schema; dropping them is the contract half of expand -> deploy -> contract and is scheduled separately. The session package's note is rewritten from a promise into the prohibition it needs to be, so nobody wires them up. `server/sessions/routes.py`'s "Ascents are not loggable here." stays: it is a route docstring, so FastAPI ships it as the OpenAPI description and it is frozen under the generated types' `openapi-sha256`. Deleting it fails `test_the_generated_types_were_built_from_this_api` (b33338e3 != 02aac3d6); verified red, then restored. It reads as a flat statement of the contract. The grade-floor filter in `web/src/profile/grades.ts` is kept — the ascent log justified one bullet about its *placement*, never the filter, and two of the three placement reasons stand alone. README item 4 is rewritten: the diary is rescoped from replaying what you did, which the plan screen already shows, to recording how it felt. Co-authored-by: Kilian Mateo <13885240+kilianmc@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…140) `journal_entry` has carried free text, three 1-5 scales and a weigh-in since the schema was written, with nothing able to fill it: no endpoint, no UI, no rows. This adds both ends. `PUT /api/journal/{client_uuid}` upserts on `(user_id, client_uuid)` with the user taken from the token, so replay-safety and authorisation are one mechanism rather than two. An unowned `logged_session_id` gets the same 404 as one that does not exist. No free text is echoed back, so nothing in the response needs escaping downstream. The `not_empty` CHECK is enforced at the edge as a 422 rather than reaching Postgres as a 500. The write form renders in two places against one draft: inline on the session summary, and in an overlay over the running player, because a note is often worth taking mid-session rather than only after Finish. Both reuse the run's uuid, so a second submit edits the row instead of adding one. The draft lives on the persisted `RunRecord` and is written on every keystroke, so a failed write cannot lose what was typed. The save control states what the press would do — absent when there is nothing to send, Save when nothing has landed (including after a 4xx), Update only when the server holds a row. Opening the overlay stops the countdown through the player's existing pause, and gives back only the pause it took: a run the climber had already paused deliberately stays paused on close. This is the app's first overlay, so it owns the machinery every later one will copy — focus in and back to its opener, a Tab trap, Escape, and a visible Close as the primary way out. Native `<dialog>` was rejected because jsdom implements neither `showModal` nor `close`, which would have left every focus and trap claim unprovable in the gate. The weigh-in input is gated on `show_body_metrics`, and an unread profile reads as off, so nothing prompts for a weight it was told not to ask for. No copy anywhere frames weight as something to reduce. No migration: DB stays at `0008` and `GENERATOR_VERSION` is untouched. The per-set body-weight snapshot stays null until #135, its only consumer. Co-authored-by: Kilian Mateo <13885240+kilianmc@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eet prose (#141) Completes #16b. The write half shipped in #140; this is the reading half. The diary is now one section per plan, each the same shape: the plan's name (renameable), then a card holding its chart, those readings as a table, and its own entries. feel/sleep/skin are RAW readings over one span told apart by stroke style, never colour alone. Entries open in a sheet and edit in place, reusing each entry's own client_uuid so an edit can never mint a second row for one session — which is why this no longer waits on #80. The smoothed weight trend was cut rather than built (Kilian), so the Body weight section is absent and the weigh-in is a column of the readings table. JournalResponse.trends is left on the wire with no consumer, recorded in the findings inbox rather than removed here. Server: GET /api/journal in two flavours, scoped to a plan and unscoped, each its own cache entry so opening the whole history and going back costs no read. The read fetches cap + 1 to tell a real truncation from a count that merely lands on the cap. PUT /api/plans/{id}/name renames a plan, refused for a demo principal. Fixes found while building: - the scope switch changed the query key, so the gate met the empty key with its loading page, the document collapsed and the browser clamped the reader to the top. keepPreviousData holds the previous scope until the new read lands, with a test that holds the read open to observe the in-flight moment. - .ct-app__chartbox rendered 300x123.75 inside a 1312px card at every screen size. Cross-axis auto margins suppress the flex stretch, so the wrapper shrink-wrapped to the svg's 300x150 default. It now carries inline-size: 100%, and its size steps 320x132 / 606x250 / 904x373 at the named screen sizes. Stylesheets now carry NO comments at all (Kilian): 1,530 comment lines removed from the 15 already-committed stylesheets, with _diary.scss arriving new and already free of them — 1,659 lines over 16 files as the sweep actually ran. The compiled CSS is byte-identical — both asset hashes match the pre-sweep build — so this removed prose and nothing else. .scss is brought under tests/test_comment_budget.py at a cap of zero, with a third detector for a trailing comment after code and no allowlist exemption, since a length argument collapses at zero. The rules that prose carried and nothing else stated are now tripwire lines in CLAUDE.md; the reasons are gone deliberately. CLAUDE.md's landing-page tripwire was stale and is corrected: text over a photograph is legal behind the --ct-scrim overlay, which the code has shipped since it was restored, and the lightest stop under copy is the contrast floor. Co-authored-by: Kilian Mateo <13885240+kilianmc@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…p stale allowlist headroom (#142) The `## Findings inbox` section is gone (Kilian). It staged a finding until the next promotion to `main`, which ties triage to an event weeks away while nothing re-checks a staged line — so it accumulates claims that have stopped being true. One of the five lines it held described a gap that the very PR which surfaced it had already closed: #141 stripped 1,657 comment lines from the stylesheets *and* shipped the `.scss` budget guard in the same commit. The rule is now one line in the header: a finding is triaged the moment it is found. The five staged findings, each routed: - the `/api/sessions/completion` off-by-one at the row cap -> filed as a register issue - the blanked-dev-server recovery -> folded into the existing tripwire as a clause - `.scss` outside the prose budget -> DELETED, already guarded by #141 - `JournalResponse.trends` having no reader -> added to the unused-columns sweep - the web suite's `settle()` race -> filed as a register issue Tripwires audited, 104 -> 103 bullets. One deletion: the gitleaks bullet, whose digest clause duplicated the stronger `openapi-sha256` phrasing and whose `useDefault` clause is asserted by `tests/test_gitleaks_config.py`. Three further clauses trimmed because a test already enforces them, and in each case the test was rewritten to own the rule rather than cite the prose being deleted — `pwaContract.test.ts` opened "Four PWA properties CLAUDE.md records", which the trim would have made false, so its four reasons moved into the `it()` names and `ABSENT_PATHS` now carries its own. Four false claims repaired in lines that stayed. `set_index` was described as the whole logged session's ordinal, which is true of `logged_set` and NOT of `prescribed_set`, where it is block-scoped by `UniqueConstraint("session_block_id", "set_index")`. A diagnostics header allowlist was asserted in the present tense and does not exist. `_sizes.scss` was cited for arithmetic that c95e03a stripped. One archive pointer did not grep-resolve. The allowlist had no deletable row, and that is provable rather than a judgement: the arm at `test_comment_budget.py:511` fails on any entry covering a comment now within its cap, and it is green, so all rows are load-bearing. What was stale is the `limit` field, which no arm checks — 41 rows carried headroom above their measured span, licensing silent regrowth (three `useSessionRun.ts` blocks of 3, 5 and 9 lines were all pinned at 26). Lowered to span. One further row was orphaned by the PWA trim and deleted, so 1,279 -> 1,278 entries. BASELINE stays 972 and `BASELINE_RATCHET` needs no move: no backlog row was removed. `npm run check` green: 1430 server, 1502 web across 62 files. Co-authored-by: Kilian Mateo <13885240+kilianmc@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Takes all ten packages from the two open dependabot PRs, pinned to the latest
stable verified against the registry rather than to what those PRs proposed.
Three land above the proposal: typescript-eslint 8.70.0, @module-federation/vite
1.21.5, @types/react-dom 19.3.0.
python: alembic 1.19.2, psycopg[binary] 3.3.5, ruff 0.16.6
web: vitest 5.0.0, eslint 10.10.0, globals 17.12.0, sass 1.104.0
@types/node and typescript keep their held-back majors per dependabot.yml.
vitest 4 -> 5 needed no config or test changes. Verified it is not a vacuous
pass: running vitest 5 against the pre-upgrade dist/ still reports 1502, so the
runner contributes zero tests. The web count moves 1502 -> 1505 because
@module-federation/vite 1.21.5 emits three further chunks reachable from
remoteEntry.js, and distContract.test.ts derives one case per reachable
artifact. Server 1430 passed, web 1505 passed across 62 files, no skips.
Four fixes to prose and config that no CI job checks:
- vitest.config.ts told readers to avoid environmentOptions.jsdom.userAgent and
use jsdom's `resources: { userAgent }`, because jsdom >=28 dropped the
ResourceLoader export vitest imported. Vitest 5 fixes exactly that, so the
prohibition is void and is deleted. Confirmed at both ends: v4.1.11 has the
unguarded `new ResourceLoader(...)`, v5.0.0 adds getResourceOptions() with a
fallback.
- distContract.test.ts carried three stale "measured today" numbers: 43 -> 51
precache entries, 22 -> 21 boot hrefs (its stated breakdown did not sum to its
own total), and a byte figure now cited as the KiB workbox actually reports.
- Naming `exclude` REPLACES vitest's defaults rather than extending them, so
**/.git/** had been silently dropped. Restored, with the reason stated.
- engines.node was `>=24.15.0`, which admits Node 25 — a version vitest 5
refuses outright. Narrowed to `^24.15.0 || >=26.0.0`.
Does not close the adm-zip advisory: it has no patched version, and
@module-federation/vite 1.21.5 still pulls adm-zip@0.6.0 via dts-plugin@2.9.0.
vite.config.ts sets `dts: false`, so that codepath does not load.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The replacement comment was 3 lines against a plain-tier cap of 2, so test_comment_budget failed in CI. Trimmed rather than allowlisted: it carries no justification an [[exception]] entry could honestly state, it was simply wordy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…g them Kilian's call: fit the cap, or write no comment at all. The three "N today" annotations in distContract.test.ts were measurements living in prose. One had already rotted — it read 43 against a true 51 — and refreshing the number just resets the clock, because the figures track the MF chunk graph and move on any @module-federation/vite bump. The executable claims are the floors (30, 15) and PRECACHE_BYTE_CEILING; those stay and are what actually guard anything. The vitest.config.ts exclude comment goes back to one line. It states a non-obvious mechanism rather than a measurement — naming `exclude` replaces vitest's defaults instead of extending them, which is why .git has to be restated — so it earns its line, but not three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
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.
Promotes
devtomain. 9.0.0 → 10.0.0. Four feature PRs plus the dependency pass, none of which has been in production.What goes live
feat(journal)— write how a session felt, from the summary or mid-runfeat(diary)— read entries back as a per-plan chartdocs— cut ascent logging from the product (#79)chore(docs)— delete the findings inbox, audit every tripwirechore(deps)— supersede dependabot #137/#138 at latest stableThe user-visible headline is the training diary: sessions can now be journalled and read back as a chart. Everything before this promotion has been invisible to production since 9.0.0.
No migration is owed
DB stays at
0008_plan_persist.journal_entrywas pre-provisioned in0004_domain_schema, so the diary feature needed no schema change and this promotion dispatches nothing.GENERATOR_VERSION deliberately stays at 9.0.0
It describes the algorithm and bumps only on a behaviour change. Nothing in this promotion touches plan generation, so bumping it would falsely invalidate existing plans —
Planpromises that version + input reproduces the tree.Dependency pass
Ten packages from dependabot #137/#138, pinned to latest stable verified against the registry — three above what those PRs proposed.
vitest4 → 5 needed no config change, and was proven non-vacuous by running vitest 5 against the pre-upgradedist/(still 1502, so the runner contributes zero tests).@types/nodeandtypescriptkeep their held-back majors perdependabot.yml.Four fixes to prose and config that no CI job checks: a prohibition vitest 5 made void, three stale measured numbers (deleted rather than refreshed — a measurement in prose just rots again), a silently-dropped
**/.git/**exclude default, and anengines.noderange that admitted a Node version vitest 5 refuses.Verification
devatf0575abNote on Dependabot
The one open alert (
adm-zip) will not close on this promotion — it has no patched version, and MF 1.21.5 still pullsadm-zip@0.6.0viadts-plugin@2.9.0.vite.config.tssetsdts: false, so that codepath never loads. Dependabot PRs #137/#138 should auto-close once it re-evaluatesdev.🤖 Generated with Claude Code