Fail-closed: a failing source logs and adds 0 minutes;
a bad key, ledger or registry exits 1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWYcmik6APnHfc9gEVy
RangeSlider.astro + <range-slider> replace the React component.
Pure helpers, tokens.css and range-slider.css are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWYdwRAtMg7ki6TKxuU
Save-as on mobile (SAF), save dialog on desktop, download on web; versioned envelope helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWXTUM9dpnCS2wtFkx4
FitGrid lays items out in a grid sized to the box it is given and pages
the overflow sideways with dots, so a screen never scrolls vertically.
First consumer: tunedeck's home tab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWZT4baL56wRDmNioxj
http's ClientException prints its request URL, so 'network error ...:
$exc' embedded ?auth=<ID token> (RTDB) or ?key=<API key> (auth) in every
FirebaseSyncError / FirebaseAuthError, which apps show and log. The
messages now use exc.message only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXYpMKCt4RFSbCMfYyAPCs
kartka's headless agent wrapper serves ~/.config/kartka-agent/
firebase_auth.json once per launch as /sync-credentials, and its weekly
source evaluator reads credential_store_for("kartka-source-eval"), which
otherwise falls back to the retired password. One run with
--app kartka-agent --app kartka-source-eval seeds both from one consent
tap. Both also join the default all-app run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2brk4NNnRyTnt7cD2QF2f
Drives a Flutter web build in a persistent headless chromium (systemd-run
unit, own profile, CDP port) through the semantics tree: tree, tap by label,
text, key, scroll, wait, shot, console. Never touches the live display;
attaches only to HeadlessChrome. The MCP server wraps the same Driver.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CZ4sULGfyav7EEwQ6vhbfg
The sufler PC TTS relay reads ~/.config/sufler/firebase_auth.json via
credential_store_for("sufler"), like tutor, so it needs a seeded session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jw52sNWquCp34dy3rj19GD
tool/coach_live_check.sh starts the real Relay (lane, backend, HTTP API,
phone mirror) on a free loopback port, never 8777, with claude, Firebase,
LeetCode, Ollama and Anki faked and every file under a temp dir
(_live_relay.py), then drives it through WrapperServer's /coach/* proxy
with WrapperCoachTransport (coach_live_check.dart):
- flow: start, step-1 partial then pass, a chat turn, code refused before
step 2, three step-2 fails via toolbox, a force, an unknown and an
over-long pick (bad_request), an also_accepted pass, run_code, end,
not_live after it; 404 for an unknown rid.
- slow: a 35 s coach turn through a 30 s proxy timeout answers, with
heartbeat.busy naming the rid and every poll under 2 s.
The flow's raw answers are committed as test/fixtures/coach_relay/ and
parsed by relay_fixtures_test.dart, so the models are checked against
what the relay sends, not what the fake's author believed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
phase.step2_attempts counted every step-2 grade with grade "fail",
and a force repeats the last try's grade, so three named-a-tool fails
plus "Continue anyway" showed attempts: 4 in the state. Measured
against the live relay through the app's desktop path; the contract
(and the snapshot's _coach_grades) already exclude forces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The RTDB mirror wrote synchronously on the lane's one worker, so a hanging
Firebase (15 s connect timeouts) would have stalled every desktop request.
PhoneLink now only enqueues; relay.outbox writes on its own thread in the
contract's order (chat reset/lines, state, heartbeat, then the result),
keeps only the latest state and heartbeat, never drops a result or a chat
line, and retries a failed write first, with backoff.
The whole-request 280 s cap is enforced again: Coach.handle sets a
deadline and a coach turn gets what is left of it (at most 240 s), so an
ambiguous message's code check plus the turn cannot outlive the phone's
300 s "No reply" wait. The contract's caps line is back to that wording.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
leetcode_coach.relay implements DOCS-coach-relay.md: one paced serial lane
(1 start / 3 s, 5 queued, done-map re-answers a rid), the loopback API on
127.0.0.1:8777 (Host check, JSON only, 16 KiB), the RTDB request/result/
state/chat/heartbeat mirror over SSE with 60 s rescans and 24 h cleanup, the
five-step state machine derived from the session log, graders (steps 1/5
haiku -> Ollama, fail open "unverified"; step 2 through PhaseGate.screen;
step 3 mechanical against answer_key.json), the CLI's sonnet chat with the
log in the child env and the local hand-over, run-code on node --permission,
and step 4 through the harvester's read-only fetch_recent_ac once a minute
(three-valued: unknown is never "not yet").
New log events session_start/grade/chat/session_end land in session_log,
_coach_log and _well_formed together; the snapshot rows gain grades and
accepted (_coach_grades). A forced step-2 grade opens code (phase.req_state).
leetcode-coach-relay.service + an install.sh step (not run).
Contract fixes in the same commit: step 3 counts also_accepted and combined
entries' parts, step-2 grade events and the force event shape, next_step,
per-call model caps instead of a 280 s watchdog, heartbeat on busy changes,
the statement fetched in-process, enable + restart on reinstall.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
The exporter used to publish coach: null before the first session, so the
tab printed "unknown" everywhere with no way forward. Now a missing or empty
sessions dir exports empty problems/re-tests, every cluster at zero with its
problems (slug, number, title, URL) and next, and a how_to block (commands,
the 4-step method, first command). null stays for a real failure.
how_to lives once in leetcode_coach/how_to.py; DOCS-how-to.md quotes it and a
test fails on drift. The app's Coach tab shows a How to start card (monospace
command box with copy), one card per cluster with tappable problems, re-tests
with their command, and "unknown" + why only for a null section. Also fixes
the done/solved key mismatch (the app read a key no exporter wrote).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
The incomplete-list note shows meanings before code, so "a ^ x ^ x = a"
or "x & -x" handed over the answer. Rewrite the six leaking meanings plus
two that said "sorted", and pin it with
test_no_property_meaning_names_an_entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
The fetched statement carries LeetCode's topic tags and site hints ("Hash
Table", "Two Pointers"), so exempting it pre-cleared the answer. Some real
toolbox meanings illustrate with the operation (self-inverse: "a ^ x ^ x"),
so the incomplete-list note shows those properties by name only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
The gates read the toolbox through toolbox.load_safe (typed, fail-open)
beside main's raw load(); the session log follows LEETCODE_COACH_SESSIONS_DIR,
stamps LEETCODE_COACH_CLUSTER, and the card builder logs postmortem on
delivery. leetcode report dispatches beside cluster_cli; client notes move
to DOCS-clients.md to keep SKILL.md under the cap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
"merge intervals" is #56's own title, so the leak bounce would have bounced
the coach's restatement of it; "dictionary" was a bare common word. A test
now pins that no entry name or alias equals any clusters.json title.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
- toolbox.json: 20 entries over a fixed 40-property vocabulary; no entry's
property set nests in another's, so every property card has one answer.
- clusters.json: decision 10's six clusters, ids plus slugs.
- progress.py (stdlib only): solved = code + postmortem, re-tests due at
+3/+10 days, cluster progress, all derived from the session logs.
- leetcode due / --cluster <name> / --retest <slug> (cluster_cli.py): due
re-tests of the cluster first, then the next problem with no code event,
skipping LeetCode-Accepted ones (live check fails open). A cold re-test
logs `retest` only once the session reached a post-mortem.
- Post-mortem cards are property -> operation only, generated by
toolbox_cards (shared slug `toolbox`); `add` appends an entry only after
kuhy confirms its properties. The local client accepts {"toolbox": [...]}.
- _coach_log publishes retests_due and clusters from the coach.
- requirements-sync treats sibling repo packages as first-party.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
The REPL passes os.environ to `claude -p`, so a PYTHONPATH set by the
launcher reached every hook and every Bash call of the coached session. The
old launcher did not do that. The launcher now runs `/usr/bin/python3 -P -m
leetcode_coach.leetcode_repl` and relies on install.sh's editable install,
the same way the Stop-hook shim does. A test now pins that the launcher
sets no PYTHONPATH.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi