Skip to content

docs(coil): Loops — design, prototypes and implementation plan (#42, #38 residue) - #120

Open
radroid wants to merge 17 commits into
mainfrom
t3code/loop-observation-thread-prototypes
Open

docs(coil): Loops — design, prototypes and implementation plan (#42, #38 residue)#120
radroid wants to merge 17 commits into
mainfrom
t3code/loop-observation-thread-prototypes

Conversation

@radroid

@radroid radroid commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Design and plan for Loops — running a T3 Coil thread unattended, and, more to the point, what you see when you come back to it. Consolidates #42 (self-paced loops) with the residue #38 left when it closed ("durability + acting on the signal"); #44 (maintainer agent) is sequenced after and shares the reactor.

Docs only. 18 files, +16,831, zero deletions, no code, no seam rows. Nothing here ships behaviour — it exists to be argued with before any of it gets built.

What's in it

File What it is
report.html the design report — 8 clickable prototypes inlined, single self-contained file, no server needed
PLAN.md the implementation plan, written to be read without repo access
BACKEND.md full backend design, including the architectures I rejected and why
TESTS.md 159 test cases
UPSTREAM-DELTA.md re-verification against upstream, and against the post-sync tree
FINDINGS.md raw research notes
prototypes/, build-report.mjs the prototype sources and the inliner that builds the report
gh pr checkout <this>
open docs/coil/loops-v2/report.html      # start here — the prototypes are interactive

Why this supersedes the 2026-08-02 design

docs/coil/loop/ was frozen on 2026-08-02 and never built. Three things have since made it out of date, and all three make the feature cheaper:

  1. Upstream shipped thread pinning (da6e1a967) two days after that design froze. A pin overrides the settled/snoozed lifecycle, so "a loop is a thread that stays at the top" is now mostly an upstream feature. The old design's long argument for a bespoke sidebar rail is correct and moot.
  2. Sidebar.tsx / Sidebar.logic.ts are not seam rows. Loop-as-pinned-thread costs zero; bespoke loop rendering would open a new row in a 3911-line file that took 7 commits in 3 days. That kills the middle option outright.
  3. AskUserQuestion already gives structured questions natively — and blocks the turn. A correct question at 01:00 costs the whole night. Hence a second, non-blocking channel beside it.

That third point got worse while this was being written. Upstream pingdotgg#5127 now settles a pending question as an empty answer when the session stops — so an unanswered question isn't merely stranded, the agent receives {}, a thing shaped like a decision that carries none, and proceeds on it. The human never sees the question at all.

The shape

  • The console is the headline. One page answering "what do you need from me?" — blocking items, deferred blockers, loop state, iteration ledger. It reads three sources, two of which need no model cooperation, so a model that never calls raise_blocker still produces a useful page. That degradation property is the acceptance test.
  • T3 backstops Claude's scheduler; it does not replace it. [Feature]: Self-paced loops — the Claude binary can already wake a T3 thread; surface it, bound it, and give models a durable T3-native wake_me instead #42's read that the scheduler is unusable is stale — the gates are on and upstream 2c7267ad4 made the reaper skip live background work, so self-paced wakes ≤30min land reliably. T3 defers when a wake is pending and covers only what an in-process scheduler cannot: wakes over an hour, wakes lost to a restart, non-Claude providers, and any notion of a budget.
  • Trigger on updatedAt staleness — never on session.status. A background subagent's message auto-opens a synthetic turn that pins status = running and nothing closes it, so gating on it deadlocks exactly the threads this is for.
  • A → C, agreed. Pinned thread first, Loops workspace later. Never the middle option.
  • spent must never read as success. "It finished" vs "it ran out of rope" is the distinction that actually failed in practice.

What it costs the fork

Three new seam rows for phases 1–4, ~7 lines, all additive, against a ledger that carries 53:

Phase File Delta
1 ClaudeAdapter.ts +1 — one spread beside the existing mcpServers one, read-only
4 settingsSearch.ts ~+4
4 SettingsSidebarNav.tsx +2
3 _chat.…$threadId.tsx ±0 — existing row rewritten in place
packages/contracts, Sidebar.tsx, Sidebar.logic.ts, server.ts 0

The settings pricing is not an estimate — upstream shipped /settings/integrations (pingdotgg#7082) three days ago, so it's read off a worked example, including which two expensive rows to avoid.

Verification

Every claim in PLAN.md is marked [V] (a command verified it) or [A] (assumed). Every [A] is a place this could be wrong, and they're listed rather than buried.

The branch was cut before the 116-commit sync landed. Main was then force-rewritten under it, so this was rebased onto the new main (f6355f06f, merge-base a4cc1367b, zero behind upstream), and re-based once more onto 94c6328ef after the daily sync force-rewrote main a second time and every load-bearing claim re-run against the fork's own post-sync tree — a stronger check than against upstream, since it's the tree the work would be built on. Recorded as UPSTREAM-DELTA.md §7. Nothing moved: the beachhead is still empty (session_crons / ScheduleWakeup / CronCreate zero across server and contracts, options.hooks unset, toolkits still only preview), Sidebar.logic.ts is still zero-diff, the overlay row is still +10/−6, pinnedAt is still in contracts.

One useful side effect: the sync also dissolved the plan's only sequencing constraint, and it answers open wayfinder issue #112 — registering a right-panel tab costs 2 seam rows.

What I want from review

PLAN.md §10 has the four questions where an outside opinion is worth most, and §11 states what would falsify each major decision, so disagreement can aim at evidence rather than taste. The two I'd most like challenged:

  • Is deference to Claude's scheduler right, or merely clever? It's more correct and more complex, and it makes behaviour depend on a hook whose delivery is still [A].
  • Should the console be loop-scoped at all, or a global "needs you" inbox from day one? The second is barely more work, is useful with loops switched off, and might be the better feature.

Build order

Phase 1 is deliberately inert — record session_crons and the durable state, act on nothing — because that is the cheapest way to test the plan's largest assumption. If the Stop hook doesn't deliver session_crons as documented, the design changes there and one additive line is the whole loss.

Phase 0 is unrelated debt this turned up: the fork has two independent mirrors of upstream's private HTTP scope-auth, in autoResume and webPush. Promote the parameterised one and let the loop routes be the third caller rather than the third paste.

Not for merge

Draft on purpose — this is the artefact for independent review, and per the original brief nothing lands on main until the feature is done. Happy to mark it ready and merge the docs early if you'd rather implementation PRs reference a merged plan.

UI changes

None — no product code is touched. The visuals are prototypes; open report.html to click through them.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 41deaab5-ec50-4322-8d44-e1fdea35df59

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

radroid and others added 13 commits August 18, 2026 15:10
Re-measures the archived Loop Watch design against the current tree and adds
the scope the user restated: a blocker/questionnaire console, loops as a
separate workspace, loops vs auto-resume and limits, loops as maintainer bots.

Three findings that move the design:

- Upstream shipped thread pinning (da6e1a9, 2026-08-04) two days after the
  archived design froze. A pin overrides the settled/snoozed lifecycle, so
  'loops pin to the top' is now mostly an upstream feature reachable through
  thread.pin rather than the DOM portal the design rejected.
- Sidebar.tsx and Sidebar.logic.ts are not seam rows today, so decorating a
  loop row opens a NEW row in a 3808-line hot file while modelling a loop as a
  pinned thread costs zero.
- AskUserQuestion already gives structured questions natively, but it blocks
  the turn on a Deferred until a human answers. An overnight loop that asks a
  correct question at 01:00 stops until 09:00, which is the exact failure the
  console is meant to prevent. The console therefore needs a second,
  non-blocking channel alongside it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Faithful to the product's real design system - tokens lifted from
apps/web/src/index.css, status hues from Sidebar.logic.ts, radius scale and
the stepped status-pulse keyframes copied rather than invented.

  p1-console      the blocker/questionnaire console, four scenarios
  p2-directions   pinned thread vs own section vs own workspace, priced
  p3-workspace    the Loops workspace in full, with a cross-loop inbox
  p4-settings     Settings -> Loops, with the master on/off gate
  p5-lifecycle    arming from the composer, and all 13 states
  p6-maintainer   a maintainer loop as the same reactor, different backlog

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BACKEND.md picks the durable T3-native scheduler and argues the four
rejected architectures, including why Claude's own cron tools cannot carry
this (session-lifetime only under a 30-min reaper, remote gate with no local
override, Claude-only, and no handle to bound it with).

The genuinely new part is the question channel. AskUserQuestion already
gives structured questions natively but blocks the turn on a Deferred, so a
correct question at 01:00 costs the whole night. A non-blocking raise_blocker
MCP tool sits beside it, and the console reads three sources - two of which
need no model cooperation - so it degrades to useful rather than to silence.

TESTS.md is 137 cases across decide/guards/sentinel/state/http/reactor/mcp
plus ten integration scenarios, starred where a case encodes a specific bug
this design exists to prevent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
report.html is self-contained: build-report.mjs inlines each prototype into
an iframe srcdoc with the shared stylesheet folded in, so the file opens from
a plain file:// path with no server and survives being moved or emailed.
Standalone copies stay linked for anyone who wants to poke at one full-screen.

Report chrome is deliberately not the product's typeface or palette - Fraunces
/ Instrument Sans / IBM Plex Mono on a coral-and-mint ground - so the embedded
prototypes read as screens rather than as more of the page.

Leads with the recommendation and ends with five decisions, each with a
recommended option, so none of it blocks starting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
p7 prices three homes for the console. Measuring the right-panel option
answered wayfinder issue #112 as a side effect: registering a right-panel tab
costs two seam rows, rightPanelStore.ts and RightPanelTabs.tsx, neither of
which the fork touches today.

The third shape is the interesting one - a global 'needs you' inbox across
every thread, which is barely a loop feature and is the only shape that keeps
working with loops switched off.

p8 covers mobile, which AGENTS.md requires a decision for and which is not a
formality here: it is the surface with no sidebar, so the row has to carry the
budget. It also settles the sidebar question, because Direction B has no
mobile equivalent at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Scheduled loops, reusable templates, reading session_crons, cross-thread
loops, per-check-in summaries, and letting a loop answer its own low-stakes
questions. Each is a live idea a first version should not carry, and each is
listed so it reads as weighed rather than missed.

The last one is the load-bearing refusal: the moment a loop can answer for
you, the console stops being a complete record of what needed a human, and
that completeness is the only reason to trust it at 9am.

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

Two corrections after review feedback.

First, the report leaned on the #38 stall as the load-bearing argument for
everything. Section 1 is now framed around what unattended work needs -
keeps going / stops on purpose and you can tell why / collects what it needs
from you - with the stall demoted to one exhibit and explicitly called out as
the rarest of five failure modes, not the axis. The most common failure is
the boring one: you cannot tell a finished run from an exhausted one.

Second, and more substantially: I inherited #42's 2026-08-07 static read that
Claude's own scheduler was unusable, and asserted it without re-measuring.
Re-measured today it does not hold. tengu_kairos_cron, loop_dynamic and a new
loop_prompt are all true, and upstream 2c7267a (pingdotgg#5677) taught the session
reaper to skip threads with live background work. Self-paced wakes under
~30 minutes land reliably, which is the normal cadence and matches real use.

So the design changes role: T3 backstops the agent's scheduler rather than
replacing it. Reading session_crons moves from phase 3 to phase 1 and becomes
part of the trigger - if the agent has a wake pending inside the threshold,
the loop stands down and spends nothing. The loop covers the case an
in-process scheduler structurally cannot: a wake that was armed and then lost
to a restart, which today leaves no trace it ever existed.

That buys one new seam row - a single additive, read-only spread of hooks into
ClaudeAdapter's existing queryOptions - argued in BACKEND.md section 11, with
a zero-seam fallback if the row is unacceptable.

Also: the settings switch defaults ON and is framed as visibility rather than
a hole to close; p1 gains a self-paced scenario; TESTS.md gains 20 cases for
deference and the cron record; and .eb blocks get white-space: pre, which had
been silently collapsing every evidence block in the report.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The design docs were written against merge-base 196c8ea (2026-08-14).
upstream/main has since moved to a4cc136 — 116 commits in three days —
so re-measure everything load-bearing before turning the design into a
plan, rather than assuming it still holds.

It holds. The beachhead is unclaimed: session_crons, ScheduleWakeup and
CronCreate return zero hits across apps/server and packages/contracts,
options.hooks is set nowhere in ClaudeAdapter, mcp/toolkits/ still holds
only preview, and not one of the 116 commits mentions a loop, cron,
schedule or automation. ProviderSessionReaper, ThreadBackgroundLiveness,
rightPanelStore.ts and the _chat route are unchanged; Sidebar.logic.ts
is still zero-diff; pinning survives intact, so Direction A is still free.

Three findings change the docs:

pingdotgg#5127 settles pending user-inputs as EMPTY answers on session stop.
AskUserQuestion still blocks, so that finding stands, but a question
nobody answers is no longer merely stranded — the agent now receives {},
a thing shaped like a decision that carries none, and proceeds on it.
That strengthens the case for a durable raise_blocker, and it means the
console cannot derive its list from hasPendingUserInput, which reads
false afterwards. BACKEND gains 9.1b; TESTS gains 7b (5 cases, 157->162).

pingdotgg#4466 disables hooks on capability probes — which confirms, by its own
stated reason, that user hooks DO run in ordinary sessions.

pingdotgg#7082 shipped /settings/integrations: a three-day-old worked example of
the exact operation the Loops settings section needs. It prices decision
3 at two small additive seam rows and shows which two expensive rows to
avoid. Caveat: settingsSearch.ts has taken 3 commits in 3 days, so it is
no longer the quiet file the earlier estimate assumed.

Also de-cite ClaudeAdapter line numbers, which drifted ~600 lines in
three days. Cite the structural anchor instead — line numbers in a
churn-12 file are a liability in a document meant to outlive a sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The design report answers "what should this be". This answers "how do we
build it, in what order, and what would prove it wrong" — the artefact to
hand to independent models before any code is written.

Seven phases, each shippable and revertible on its own. Phase 1 is
deliberately inert: it records session_crons and the durable state and
acts on nothing, because that is the cheapest way to test the plan's
largest assumption. If the Stop hook does not deliver session_crons as
the SDK types document, the design changes there and one additive line
is the whole loss.

Total upstream cost for phases 1-4 is three new seam rows, ~7 lines, all
additive: one spread in ClaudeAdapter, two small settings rows. The
overlay row is rewritten in place at delta zero. Contracts, Sidebar.tsx
and Sidebar.logic.ts stay at zero — a loop is a pinned thread. Against a
ledger that currently carries 51 rows, that is the number worth arguing
about, so it is a table rather than a sentence.

Two sections exist for the reviewer rather than for us. §10 states the
four questions where an outside opinion is worth most — chiefly whether
deference to Claude's scheduler is right or merely clever, and whether
the console should be loop-scoped at all rather than a global inbox. §11
states what would falsify each major decision, so disagreement can aim at
evidence instead of taste. Claims are marked [V] where a command verified
them and [A] where they are assumed; every [A] is a place this is wrong.

Phase 0 is debt: the fork now has two independent mirrors of upstream's
private HTTP scope-auth, in autoResume and webPush. Promote webPush's
parameterised form and let the loop routes be the third caller rather
than the third paste.

Report gains an addendum recording the re-verification, and its counts
are corrected (157 -> 162 cases).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Main was force-rewritten under this branch while the plan was being
written — the daily sync landed its 116-commit rebase, so the branch's
base df027ec stopped being an ancestor of main and the two shared only
the upstream merge-base. That is the documented force-landing hazard,
and opening a PR from it would have shown 175 commits and a conflict
that cannot be resolved on the branch. Rebased the nine docs commits
onto the new main instead; they touch only docs/coil/loops-v2, so it was
clean. Recovery tag coil/pre-rebase-loops-20260817.

The landing is good news twice over. Main now sits on merge-base
a4cc136 — exactly the tree UPSTREAM-DELTA measured against — and is
zero commits behind upstream, so those findings describe the fork's
current main rather than a future one. And the plan's only sequencing
constraint dissolved: phase 4's settingsSearch.ts row had to wait for
the sync carrying pingdotgg#7082 or it would collide with the integrations entry
on the way in. That entry is now in the tree, so the row lands beside it.

Re-ran every load-bearing claim against the fork's own post-sync tree,
which is a stronger check than against upstream, because it is the tree
the work would be built on. Nothing moved: the beachhead is still empty
(session_crons, ScheduleWakeup, CronCreate all zero across server and
contracts; options.hooks unset; toolkits still only preview), the
mcpServers spread anchor is present, Sidebar.logic.ts is still zero-diff,
the overlay row is still +10/-6 as costed, pinnedAt is still in contracts,
and phase 0's debt is still two scope-auth mirrors. Recorded as
UPSTREAM-DELTA section 7 so a reviewer can see the check rather than
take the claim.

One number did move: the seam ledger re-baselined to 53 files,
+2590/-1042. Phase costs are re-quoted against it. The line counts fell
across a 116-commit range, which is upstream absorbing fork work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two-axis review (repo standards + spec fidelity vs #42/#38) found:

- TESTS.md enumerates 159 cases, not the 162 its headlines claimed (the
  three coverage gates in its §11 were being counted as cases).
- Sidebar.tsx is 3911 lines on the verification tree (f6355f0), not the
  pre-sync 3808 the [V] claims carried.
- PLAN's "zero commits behind upstream" is now dated to its verification.
- The superseded docs/coil/loop design never pointed at its successor;
  its banner now does.
- FINDINGS cited ClaudeAdapter.ts line numbers against UPSTREAM-DELTA
  §5.1's own advice; the citation now carries the drift caveat.
- The working-glyph spin was the package's one un-stepped infinite
  animation (AGENTS.md: they peg the GPU on high-refresh displays).
- A p1 handler had two identical branches; collapsed.
- The plan silently dropped three things #42 asked for. It now records
  the divergences explicitly — Phase 1d's CLAUDE_CODE_DISABLE_CRON
  toggle, experiments B/D, and wake_me — each with its reason, and
  schedules the vocabulary registration domain.md asks for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Maintainer feedback on the prototypes: the coloured leading rail read as
chrome foreign to the app. A loop row is now a plain bordered card in the
sidebar's existing language — border, card background, the arc glyph —
plus a small "Loop" tag on rows that don't already sit under a Loops
group header. Colour stays where it always was: on the status system.
The mobile list rows get the same boxed treatment, and the prose that
described the rail follows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…classes, stale cites

A second independent pass over the previous two commits found: the §
in PLAN's test-strategy line had become U+FFFD (introduced by the very
commit correcting the count); the restyle removed the rules for the
loop-state row variants but left the now-inert classes in four
prototypes' markup, plus one dead classList.remove in p1; FINDINGS
still carried the pre-sync 3911-vs-3808 Sidebar measurement in two
spots and cited a §5.1 that is really §5 item 1; and PLAN's Experiment
D parenthetical misdescribed #42's experiment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@radroid
radroid force-pushed the t3code/loop-observation-thread-prototypes branch from 66a53ca to e4ab571 Compare August 18, 2026 19:11
@radroid
radroid marked this pull request as ready for review August 18, 2026 21:42
radroid and others added 3 commits August 18, 2026 18:04
…s package

A five-axis review (standards / spec / claim re-verification / internal
consistency / prototype QA) turned up 102 findings, ~60 unique. The
load-bearing ones, all verified against the code before editing:

- The compose-don't-replace correction was applied unevenly: the check-in
  prompt still said "I am the scheduler for this thread. Do not schedule
  your own wake-ups", the report kept the CLAUDE_CODE_DISABLE_CRON switch
  PLAN drops, and the report's deferred table still had session_crons in
  Phase 3. All now carry the composed story.
- Deference now matches #42's Guard #15: T3 stands down while a recorded
  wake is pending regardless of distance (the binary clamps at 3600s),
  and fires wake_lost only past a new wakeGraceMs. TESTS 11c reworded.
- session_crons carries a cron EXPRESSION, not a timestamp
  (SessionCronSummary in sdk.d.ts 0.3.170) — the nextFireAtMs parse is
  the fork's and is now an explicit [A].
- pingdotgg#5127's voiding was overstated: the aborted branch resolves the
  Deferred with {} but then DENIES the tool call while the session stops.
  What survives is user-input.resolved with empty answers — voided reads
  as answered downstream, which is still why the durable record exists.
- The §1.4 "second kill" is gone upstream (pingdotgg#5710, e70cdb4): the
  no-turnState branch emits token usage plus a log, no lifecycle event.
- total_cost_usd is stamped onto turn.completed.totalCostUsd, not
  "unread"; nothing aggregates it, and its semantics stay [A].
- Numbers re-stamped on the a4cc136 ledger: ClaudeAdapter churn 16 /
  ~4.6k lines, orchestration.ts 20, settingsSearch.ts 14, SettingsPanels
  36/2088, contracts/settings 26, the _chat row churn 4 / risk 64 (the
  fork's added delta stays 0), Sidebar +184/-81. #112 answered with
  measured churn (RightPanelTabs 14, rightPanelStore 7).
- Seam accounting unified on PLAN §6 (3 rows, phases 1-4);
  SETTINGS_SECTION_ICONS attributed to SettingsSidebarNav.tsx; the
  "search-invisible" claim dropped (the id union is derived).
- Fork main SHAs are rewritten by every sync, so the docs now anchor on
  the merge-base; the 08-18 force-rewrite to 94c6328 is recorded.
- Report section refs renumbered after the mobile insert (§13→§14 etc.),
  the 137-sum test table now sums to 159, the orphaned decision-4 chips
  and the four-vs-five scenario counts fixed.
- Prototypes: tokens re-lifted where they had drifted ([data-app-sidebar]
  values, sky-300 not sky-400, computed base literals), p5 gains the
  self-pacing and wake-lost states (15 total), the spinner became the
  product's stepped pulse, switches got role/aria, p2 prices the sidebar
  row chrome honestly (deferred — it would be a new Sidebar.tsx row),
  p6/p3 align with #44's v1 contract (never pushes, PRs opt-in later).
- build-report.mjs: function replacer, escapeHtml on marker fields, a
  GENERATED banner in the output, honest self-containedness claim, +x.
- LoopRecord gains the checkIns array the console's ledger reads;
  workSource leaves phase 1 (lands with #44); stop-with-pending-crons
  now calls stopSession, the honest cancel from #42 1c.
- The archived loop/ design's DESIGN.md banner no longer overclaims, and
  its report.html carries a superseded pointer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every file in the package had escaped the vp fmt staged hook — 17 of 18
failed --check. Pure formatter output, no content change: sources
formatted, report.html rebuilt from the formatted sources, then
formatted itself (the committed pipeline is build → fmt; fmt is
idempotent over the result). The archived loop/ report and DESIGN.md
ride along for the one-line banners the previous commit added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tions

Nine residuals, all places where a correction landed in one file and its
echo survived in another: the old inside-the-threshold deference wording
in BACKEND §1.1 and report §9 (the rule is defer-while-pending, overdue
by wakeGraceMs); §9.1b still had the agent "carrying on" on an empty
answer (it is denied and the session tears down — the projection-side
indistinguishability is what survives); the stopSession-on-terminal rule
now appears in PLAN Phase 2 and TESTS 99, not only BACKEND; decision 3
dropped the routeTree regen it elsewhere prices at zero; §0's total no
longer counts a fork-owned file as upstream surface; the §9 sub-head no
longer sequences the dropped kill switch; FINDINGS' citation of the
archived design restores that design's own t3x.loop.skipped name; and
the settings surface is nine route files, not seven.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@radroid

radroid commented Aug 18, 2026

Copy link
Copy Markdown
Owner Author

Review — two axes, then fixes (pushed as 8d87b8b59bf947b06d)

Ran the review this PR asked for: standards vs the repo's docs (AGENTS.md, docs/coil/agents/*, SEAMS conventions), spec vs #42 / #38's residue / #44 / #112, plus a claim-re-verification pass that re-ran every checkable [V] against the post-sync tree, an internal-consistency pass, and static QA over the prototypes and build-report.mjs. 102 raw findings, ~60 unique after dedupe; every fix below was verified against the code before editing. All fixes are on the branch; the design itself was not relitigated — Q1–Q4 in PLAN §10 are still yours to decide.

Standards

  • Formatting: 17/18 files had escaped the vp fmt staged hook. Now formatted (2c995f5d0); committed pipeline is build → fmt, idempotent.
  • Baseline citation: the docs anchored on fork-main f6355f06f, which the next daily sync force-rewrote away. Now anchored on the merge-base a4cc1367b, with the 08-18 rewrite to 94c6328ef recorded (same merge-base; the two extra upstream commits touch nothing load-bearing).
  • AGENTS.md conflicts: TESTS.md endorsed the repo-wide test command (now: targeted vp test run, CI owns the suite); the prototypes' spinning glyph is now the product's stepped pulse; a11y basics added (role="switch"/aria-checked, labels on icon-only buttons, p6 heading order).
  • Stale figures re-stamped on the a4cc1367b ledger: ClaudeAdapter churn 16 / ~4.6k lines, orchestration.ts 20, settingsSearch.ts 14, SettingsPanels 36/2088, contracts/settings 26, the _chat row churn 4 / risk 64 (added delta still 0), Sidebar +184/−81, nine settings routes, feat(web): configurable browser defaults in Settings → Integrations pingdotgg/t3code#7082 landed the same day.
  • docs/coil/loop/report.html now carries a superseded banner; DESIGN.md's banner no longer overclaims (feat: scheduled tasks (automations) pingdotgg/t3code#3638 verified still not on upstream/main; feat: native subagent & workflow observability pingdotgg/t3code#5219 named).

Spec

Three claims did not survive re-verification against the code and are corrected everywhere they appeared:

  1. fix(server): settle pending user-input requests when a Claude session stops pingdotgg/t3code#5127: the aborted branch resolves the Deferred with {} but then denies the tool call while the session stops — the agent does not "proceed on a null choice". What survives (and still justifies the durable record): user-input.resolved carries empty answers, so a voided question is indistinguishable from an answered one downstream.
  2. §1.4's "second kill": upstream fix(server): stop Claude resume handshakes from completing turns that never ran pingdotgg/t3code#5710 (e70cdb478) removed the untargeted turn.completed — that branch now emits token usage + a log only. The turn-boundary rejection now rests on the Stop-path kill and fails-open.
  3. total_cost_usd is stamped onto turn.completed.totalCostUsd, not "unread"; nothing aggregates it, semantics stay [A].

The compose-don't-replace correction had been applied unevenly — the check-in prompt still said "I am the scheduler… do not schedule your own wake-ups", the report kept the kill switch PLAN drops, and session_crons was still "Phase 3" in three places. All unified on the composed story. Also: session_crons delivers a cron expression, not a timestamp (SessionCronSummary in sdk.d.ts 0.3.170) — the nextFireAtMs parse is now an explicit fork-side [A].

Four spec-conformance changes worth your eyes (each follows #42/#44 rather than my taste):

Counts now agree everywhere (159 test cases incl. the report's table, fifteen guards, five p1 scenarios, fifteen p5 states — self-pacing and wake-lost added), section refs renumbered after the mobile insert, the orphaned decision-4 chips are gone, prototype tokens re-lifted where they'd drifted ([data-app-sidebar] values, sky-300), and build-report.mjs got a function replacer, escaping on marker fields, a GENERATED banner, and +x.

Loose end: #112's answer (RightPanelTabs churn 14, rightPanelStore 7, no extension point — the tab switch is the only way in) now lives in UPSTREAM-DELTA §4; worth copying onto the issue when you close it.

Review + fixes by Claude Fable 5 (Claude Code); finder/fixer passes ran on Opus.

… gate requires it

The fork's oxlint plugin errors on named imports from node builtins
("Import node:fs as a namespace named NodeFS") — the three named imports
here were the branch's real CI-lint failure, previously masked by the
subagentFanout flake landing first. Output byte-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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