Repository navigation
Description
Outcome
A thread shows which HQ Build Spec or Ticket it is working on, and Build Specs and Tickets mentioned in chat are live links. Hovering either shows a card with the item's current state from Build, so the person can see what a thread is for and how its work stands without opening HQ.
Today the header shows only project, workflow chip and title, and a Build link in chat is plain text.
Design (agreed from the prototype, 16 Oct)
Header segment. The breadcrumb gains one segment between the workflow chip and the thread title: Project / [workflow] / ◇ <Spec title> ● / <thread title>. A Ticket shows ▢ instead of ◇. The segment shows the item's title, truncated, with a status dot:
- ring: not started (inbox, shaping, ready);
- filled amber: in progress;
- filled green: landed;
- green with a halo: accepted;
- gray: dropped.
Two overlays sit beside the dot, separate from status:
- ⏸ Held (orange): the item has an open hold.
- ⛓ Blocked (red): Build reports an unresolved wait on it (
hq_build_waits).
T3 never works out blocking from statuses itself: a Spec blocker clears only when it is accepted, and Build has its own rules for Tickets blocking a Spec.
Clicking the segment opens the item in HQ, and its menu offers Change (paste a Build URL or number) and Unlink. The segment is absent when no item is linked. Link Build item sits with the thread's other actions on web (the header's thread menu and the command palette) and in mobile's thread menu.
Spec card, shown on hover after the PR preview's delays:
- Header:
◇ Spec · <spec kind> · <horizon>and status. - Title and the three-line Build summary.
- A segmented progress bar of its Tickets by status, with counts.
- Up to five Tickets with their status, then "+N more".
- A banner for a hold (its kind and note) or for acceptance.
- Rows for acceptance and governing Decisions.
- Footer: the lead thread, with its activity (active, quiet, stalled, ended), as a T3 thread link, and when the item was last updated.
Ticket card:
- Header:
▢ Ticket in ◇ <Spec>and status. - Title and summary.
- A banner for a hold, or for a block: "⛓ Blocked by (). Starts when it <is landed | is accepted>." Which one comes from the wait's
required. - Rows for Blocked by, delivery attempt and last failure.
- Footer: the worker thread, or "No thread working on it".
Chat. A Build item mention in a message, either a link or #<number>, renders as a chip: the kind glyph, the item's live title and its status dot, falling back to the link text while loading. Hovering the chip shows the same card.
Mobile. The linked item appears as the header subtitle. Tapping it, or a chat chip, opens the card as a sheet.
Settled constraints
-
Build is the only source. T3 stores which item a thread is linked to and nothing about the item itself.
-
The link is thread metadata. It is set with the existing
thread.metadata.updatecommand, like the workflow and linked PR. Its value is the Build item's id and number and the HQ origin it was resolved against. Older events must stay readable, and the link must survive restart and replay. If the project's HQ origin changes, the link shows as unavailable; it is never resolved by number against another HQ. Agents set and clear it throught3_thread_update, and a lead can set it on its delegated threads. People set and clear it from the segment menu. Both are one service method, and agents, scheduled tasks and the UI all reach it. Setting replaces any earlier link and clearing removes it. T3 never infers or overwrites a link on its own, including from Build sessions. -
The server reads Build; clients never touch HQ. The T3 server reads through the HQ MCP with its own
observertoken (ci: use blob:none to reduce checkout data pingdotgg/t3code#2485), which it exchanges from the delegation Inject a per-agent YardSmith HQ MCP connection for each T3 thread #74 stores. This works whether or not any provider is running. It uses the HQ origin and delegation of the thread's project, as configured for Inject a per-agent YardSmith HQ MCP connection for each T3 thread #74. The cache is keyed by HQ connection and item, never by number alone. The observer is ended, not revoked, when no project uses that connection. Card data reaches clients over the existing WebSocket, so local, remote, tunnel and mobile clients all work. A project without HQ configured shows no segment, and its Build links render as ordinary links. -
Freshness by polling, only while visible. Build has no change feed. While some client shows a segment or chip, the server re-reads its item and its waits once a minute, since a blocker's change does not move the item's own revision. While a card is open, its detail is re-read on the same cadence. A first read, or opening a card, reads at once, and only the periodic re-reads are rate-limited. A closed card or a segment no one is viewing costs nothing. This works independently of thread settlement: an idle thread's segment stays fresh while it is on screen. The header needs only title, number, kind, status, holds and waits, and that compact read is all that refreshes for a segment or chip. The card's detail (Tickets, attempts, acceptance, Decisions, sessions) is read when a card opens. Neither read uses
hq_build_context, which returns full bodies. The server sends a client only the card fields, never item bodies. -
Thread links come from Build. The lead and worker threads on the cards come from Build's sessions, not T3 metadata:
- The lead is the coordinator session (
parentSessionIdnull) of the Spec's current bind generation. When none is open, it is the most recently ended one, shown as ended. - A Ticket's worker is its open helper session in that generation whose
ticketIdsinclude it, with the latestlastActivityAtwinning. When none is open, it is the most recently ended one, shown as ended. With no session at all, the card says "No thread working on it". - A session with harness
t3andsessionRef<environmentId>/<threadId>becomes a T3 thread link, rendered like at3-thread://chip, including its handling of an environment this client is not connected to. - Any other harness or reference shows as plain text with its activity.
- An ended session shows as ended.
Recording that
sessionRefis the producer's job: the lead's bind or start_session and its delegate_session calls. - The lead is the coordinator session (
-
Recognised mentions, in projects with HQ configured, using that project's HQ origin:
https://<HQ origin>/build/<number>andhttps://<HQ origin>/build/specs/<number>links;#<number>, HQ's own way of writing a Build item, for numbers of seven or more digits. Build numbers start at 1,000,000, so GitHub references such as#78never match.
Mentions inside code spans and code blocks stay as written. A
#<number>that Build doesn't know stays plain text. Numbers without#, and slugs, are not recognised. -
Unavailable HQ. If HQ is unreachable or the token is revoked, the segment keeps the last known title, and the card says Build couldn't be reached and when it was last updated. With nothing known yet (after a restart, say), the segment shows
Build <number>with a gray dot. It never shows a spinner indefinitely. -
No continuously running animation. The PR preview's hover card (
apps/web/src/components/pullRequest/PullRequestLinkPreview.tsx) and the thread link chip (apps/web/src/components/chat/MarkdownThreadLink.tsx) are the patterns to follow.
Acceptance
- An agent links its thread to a Spec with
t3_thread_update, and the web and mobile headers show its title and status. A person links an unlinked thread from Link Build item, and changes or unlinks it from the segment menu. Linking again replaces the link. The link survives a server restart. - Against Development HQ data from
seed:hq:- The Spec card shows the summary, Ticket counts by status and up to five Tickets.
- A held item shows ⏸ Held and its hold note.
- A Ticket blocked by an unfinished item shows ⛓ Blocked and names the blocker.
- A Ticket blocked by a Ticket clears ⛓ Blocked once the blocker lands. A Ticket blocked by a Spec stays blocked when that Spec lands, and clears once it is accepted. Both are tested against Build's waits.
- Given sessions recorded with harness
t3and a<environmentId>/<threadId>sessionRef, the footer links to the lead or worker thread and opens it. A non-T3 reference, or an environment this client doesn't know, shows as plain text. An ended session shows as ended. - A Build link or
#<number>in an assistant or user message renders as a live chip with the same card.#78, a mention in code, and an unknown number stay as written, and in a project without HQ links stay plain links. - With a controlled clock, tests show the server re-reads an item and its waits only while a client shows it, once per minute, and the card detail only while a card is open. First reads happen at once, and clients receive card fields but never bodies.
- With HQ unreachable or the observer token revoked, the segment and card show the last known state and say Build couldn't be reached.
- On mobile the item shows as the header subtitle, and tapping it or a chat chip opens the card sheet.
- The user guide gains a short section on linking a thread to Build work.
Non-goals
- Editing Build from T3 beyond linking and unlinking: no status changes or holds.
- Searching or browsing Build inside T3. Change takes a URL or number.
- Recognising numbers without
#, or slugs, in chat. - Linking a thread to more than one item.
- Upstream (pingdotgg/t3code) support. This is fork-only, behind the project's HQ configuration.
- Agent guidance to link threads, record T3
sessionRefs and refer to Build items as#<number>. That is pechhe/agent-config#7.
Relations
Blocked by pechhe/yardsmith#2485 (Build summaries, item URLs and the observer role) and by #74, which stores the delegation and exchanges tokens.
Activity
pechhe commentedon Oct 16, 2026
Shaped 16 Oct. Decided by the owner: Build is the only source of item data; the item appears as a breadcrumb segment; Held and Blocked are separate overlays, not statuses. A standalone HTML prototype of the segment and both cards was agreed and is now recorded in the Design section. The prototype itself was disposable and has been deleted. A read-only advisor challenged two drafts. Its findings are settled in the body: the observer token, compact vs detail reads, waits as the source of Blocked, session selection, a first-link entry point, and cold and unavailable states. Tickets: #79 → #80 → #81 and #82. Blocked by pechhe/yardsmith#2485 and #74.
pechhe commentedon Oct 17, 2026
Spec review (spec-reviewer, Opus, fresh context)
docs/user/build-items.md)#ninside raw HTML<code>rehypeBuildMentions. Escaped\#nstays out of scope.mentionsreportsunavailableacceptedAt, which the card shows as a banner, and the status already shows when a Spec isn't yet accepted#110 and #111 each passed a ticket review. #109 is docs only.
Evidence gaps, not blocking: the chat chip's hover card and the #82 footer thread links were not seen rendered against an HQ with Build data. The chip uses the same card component as the header segment, which was checked against Development HQ in #80. Mobile has not been checked on a device.
pechhe commentedon Oct 17, 2026
All Tickets (#79–#82, #109–#111) have landed and the Spec review's findings are answered. See the review comment above.