The mount-once bootstrap effect keeps its literal `}, []);` dependency array:
two existing contract tests use that exact text as a slicing landmark, so the
effect stays as it was and its existing exhaustive-deps warning simply names two
more stable setters. Only the signOut slice marker in
chat-navigation-a11y-contract moves, with the three-column note; the assertion's
subject is untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
5.3: profile / profileDraft / profileNotice / avatarNotice / avatarSaving /
profileSaving move into use-profile-onboarding.ts; Home reads them back from the
hook's return. The hook call moves above useBirthTimeGuidedJourney so profile is
defined before the first reader.
Turning the hook stateful made the React compiler lint rules apply to it, which
surfaced five errors that only a hook-shaped file gets checked for:
- two ref params written through .current (dialogReturnTarget,
birthTimeRevisionPending) now end in "Ref" as the rule's hint asks;
- two render-phase ref writes (guidedBirthTimeReadyRef /
editDeclaredBirthTimeDetailsRef) are gone: the hook already returns
completeGuidedBirthTime and editDeclaredBirthTimeDetails, and now that it runs
before useBirthTimeGuidedJourney they are passed straight in. Both callbacks
were only ever read from render-scope closures, so the call target is the same
function on every frame as before.
setProfile / setProfileDraft are listed in the two effects that call them; they
are raw useState setters handed through the return, so identity never changes
and neither effect gains a re-run.
Zero behavior change, zero copy change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
5.1: memoize createRectificationShellSetters so the four shell setters keep a
stable identity across renders, then list the two the entry-summary effect uses
in its dependency array. Lint warnings 120 -> 119 with no new warning.
5.2: move the four synastry useStates and draftSynastryQuestionFromChart into
the new useSynastry hook. Home keeps only two stable callbacks the chart-library
effect calls; the chart-library dialog receives one spread synastryPanel object.
Zero behavior change, zero copy change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
Home() useState 66->52, useRef 41->39. Shell-facing fields merge into one object;
subtree state lives in useRectificationSurface. Chat props 20->7. Growth contract
rebaselined. Zero behavior/copy change.
Move BUG-705 bootstrap selection + session lookup into
resolveLookupBootstrap so Home only awaits once. The 1951 cap in
chart-view-route.test.ts is unchanged.
Rectification answers now advance chat_sessions.updated_at (BUG-704).
A ?c= id missing from the loaded page is fetched before anyone may call
it deleted (BUG-705). The range card no longer has a post-card tie-break
button; live questions leave the card visible with adopt locked
(BUG-706/708). Spoken copy bans 相对支持度 (BUG-709).
Task docs assigned 700-704; qizheng already took 700-703.
Homepage no longer waits on /api/onboarding for six starter questions.
Natal answers show the spoken layer first; the Level 2 skeleton sits in a
collapsed 完整分析 block. Wide tables scroll sideways on a phone. Form
inputs are 16px so iOS does not zoom on focus.
Session open was sending the live registry version into v2, so every Skill
bump rejected older cases with an unmapped 500 and no error on the clicked row.
Co-authored-by: Cursor <cursoragent@cursor.com>
Bare `/` was treating the latest history session as active, so a returning account could auto-open a failed rectification Case and left-align the starter home.
Co-authored-by: Cursor <cursoragent@cursor.com>
The four account panes now share a fixed frame, chart profiles open as list then detail, and membership lives in the homepage dialog instead of a separate page.
Co-authored-by: Cursor <cursoragent@cursor.com>
Enter now queues one follow-up instead of dropping it, and a rectification stop leaves the streamed reply with a grey notice instead of an error alert.
Co-authored-by: Cursor <cursoragent@cursor.com>
Opening a rectification Case used to switch sessions first and read the
turns afterwards, so the reader saw a plain transcript, then an empty
panel, then a remount when the turns arrived (and again after the first
turn settled, because the panel key carried a ready/loading suffix).
The surface hook now reads turns and snapshot in one Case request under
the home reveal budget and only then makes the session active; the
sidebar defers the switch the same way; a session selected at bootstrap
(deep link, refresh) is hydrated during the prepare phase so the reveal
shows the surface itself; the panel key is the session/Case binding only,
later turns fill an empty transcript as a prop update, and unmounting
aborts any stream or snapshot read. The homepage card and the sidebar row
say 正在打开 statically while the Case opens — no spinner after the reveal.
BUG-505
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
Public can_adopt no longer starves the collect fallback, 改选 scrolls to
the offer, opening collect uses other, and probe_year stays on the
schema. The status bar no longer starts an empty consultation chat.
Co-authored-by: Cursor <cursoragent@cursor.com>
The loading screen now owns every wait: starter questions, today's
starlanguage and the rectification entry summary are fetched in a prepare
phase before reveal (4s budget, static fallbacks), the rectification chunk is
warmed, recent sessions are prefetched, and no component spinner remains
after the page appears. BUG-479.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
The rectification session rendered its own textarea (no length ceiling,
no count), its own rAF scroll follow with a sticky-scroll helper, and its
own jump chip styled differently from the main chat's inline Tailwind
button, while the main chat re-ran scrollTo on every streamed token and
added a smooth scroll on settle. ChatComposer now accepts a controlled
value so the rectification surface reuses it without touching the main
draft store; useConversationScrollAnchor owns the follow through a
ResizeObserver, one frame per content change, for both surfaces; a
single JumpToLatestButton replaces both chips; the 720px transcript
override is gone. DESIGN.md records the shared composer, the jump
control, the reading width and the two shadow tokens.
BUG-477 BUG-478
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
eslint-plugin-react-hooks now analyzes the smaller Home, so render-time ref writes and sync effect setState were failing the staging gate.
Co-authored-by: Cursor <cursoragent@cursor.com>
Close the home-page split by moving account/onboarding and rectification glue out of page.tsx without relocating React state, keeping the route static and hook order intact.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep archive as a PATCH of archived_at rather than DELETE, pass entry_mode through onboarding, and let popstate to the default chat reuse selectSession. Then move send/stop/recovery and session management out of page.tsx so the home surface stays within the batch-two line budget.
Co-authored-by: Cursor <cursoragent@cursor.com>
page.tsx still owns the chat main chain, but the first product surfaces now
live in their own modules so later splits can land without editing the 4k-line
Home. Source-lock tests follow the moved tokens; the orphan user-data contract
is aligned and added to the quick gate.
Co-authored-by: Cursor <cursoragent@cursor.com>
Local fallbacks were creating fake saves and resurrecting deleted rows.
Pin and archive now live on chat_sessions so they follow the account.
Co-authored-by: Cursor <cursoragent@cursor.com>
Refresh, back, and login return were dropping the open conversation because selection lived only in React state.
Co-authored-by: Cursor <cursoragent@cursor.com>
List GET no longer ships transcripts; consult appends questions after reserve and ignores client history so dual-tab last-write-wins cannot erase messages.
Co-authored-by: Cursor <cursoragent@cursor.com>
Dark muted surfaces missed WCAG AA; action and tertiary tokens plus a 32-pair contract close that. Hit targets, remaining-count, and Enter-to-send follow the interaction audit without changing visual sizes.
Co-authored-by: Cursor <cursoragent@cursor.com>
The onboarding paywall wrote `dialog-header`, a class no stylesheet
defines, so its close button stacked under the title instead of sitting
beside it. The session-delete confirm buttons had the same problem, and a
`message-entry` wrapper added in August had quietly killed the
`.message + .message` spacing rule in the main chat.
Fixes all three against the patterns already in the codebase, then adds a
contract test so the next undefined class fails instead of shipping: it
scopes itself to our own class families so Tailwind utilities stay out,
strips CSS comments before deciding what counts as defined, and carries
an allowlist of the fifteen deliberate no-op modifiers that must shrink
rather than grow.
Also collapses the paywall's duplicate display heading into the intro
sentence, leaving one heading in the dialog.
BUG-434, BUG-435, BUG-436.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
Self-hosted PostgreSQL is the runtime. A missing APP_DATABASE_URL
must not tell users the retired hosted service is unconfigured.
Co-authored-by: Cursor <cursoragent@cursor.com>
Settled messages no longer rebuild on every token, Inter is actually
requested, and admin routes drop the 33 KB chat stylesheet. Root
force-dynamic is gone so public shells can prerender without changing
the no-store Cache-Control contract.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep provider thinking on a separate channel so process talk is not billed as the spoken reply (BUG-359).
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep provider thinking off, classify CoT as 思考, and stop remounted sessions from firing a second opening.
Co-authored-by: Cursor <cursoragent@cursor.com>
Disable provider thinking so Flash CoT cannot fill max_tokens, raise the
spoken budget to 16384, emit a server-owned step tree, and continue once
when the body ends on length.
Co-authored-by: Cursor <cursoragent@cursor.com>
Users pick a clock range instead of a coarse period plus notes, so
rectification and window consult scan that range instead of a leftover afternoon bucket.
Co-authored-by: Cursor <cursoragent@cursor.com>
Narrow screens were wrapping 已执行 into a glyph column and painting every sidebar row the same gray, so history looked like top-level chrome.
Co-authored-by: Cursor <cursoragent@cursor.com>
Thinking disappeared on failure and never reached session storage. Keep the
sanitized chain on disk and on errors, and regroup the sidebar around reports,
charts, favorites, and dated history titles.
Co-authored-by: Cursor <cursoragent@cursor.com>
Clicking today's reading or a topic card should open a consultation and wait for the model, not leave the question in the input box.
Co-authored-by: Cursor <cursoragent@cursor.com>
Enumerate evidence kinds so education cannot be proposed as a kind, and stream Chinese thinking on a separate channel that collapses when the reply arrives.
Co-authored-by: Cursor <cursoragent@cursor.com>
Rectification stays optional. Reported minutes can consult and generate reports; date-plus-period uses a declared window instead of a midpoint or 00:00. Updates BUG-341.
Co-authored-by: Cursor <cursoragent@cursor.com>
Rectification dropped tool.activity started events and treated length finishes as completed. Share generation settings with consultation, keep the activity line through streaming, and name multi-domain chart calculation.
Co-authored-by: Cursor <cursoragent@cursor.com>
Quality gate lint failed on setHeaderSlot inside useLayoutEffect, so staging never published. Pass the header mount node from a callback ref instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
Homepage bootstrap never escaped the loading shell when a new web image changed chunk hashes. Stamp deploymentId, stop caching chat HTML, and hard-reload failed chunk loads once.
Co-authored-by: Cursor <cursoragent@cursor.com>
Put the compact board trigger in the chat header, keep the sheet above the composer on iOS, and align onboarding messages with the intake card on the session column.
Co-authored-by: Cursor <cursoragent@cursor.com>
The board was live data sitting under chat, so each new event looked like nothing changed. Keep candidate cards in the transcript and group 换升 by minute for the side panel.
Co-authored-by: Cursor <cursoragent@cursor.com>
Surface D9/D10 change minutes, event-dasha match copy, dual-dasha conflict, and a post-adopt consult handoff so users can keep narrowing or start a reading from a representative time.
Co-authored-by: Cursor <cursoragent@cursor.com>
Incomplete Flash generations were billed as completed consultations. Fail
those runs, keep the partial text, and reuse the rectification like/copy/rerun
bar on ordinary chat replies.
Co-authored-by: Cursor <cursoragent@cursor.com>