docs(attr-child-clock): add reference findings and the feasibility fact

This commit is contained in:
Brennan Benson
2026-09-17 12:07:21 -07:00
parent 1a49857542
commit eeceea7270
+73
View File
@@ -89,3 +89,76 @@ Say which category your change is in.
does not.
4. Propose a mechanism; prefer architecture over a guard. State whether it removes the bug class.
5. Mark every claim VERIFIED or UNVERIFIED. Cite file:line.
---
# Reference findings
Five independent implementations were reviewed. Projects are deliberately not named here; describe
any conclusion in repo-native terms only. Every mechanism below was read at source and verified.
## Converged — all five agree
1. **A child's activity never touches the parent row's lifecycle state, label, or timestamp.**
2. **Attribution happens at ingestion (the producer boundary), never as a reader-side filter.**
3. **Exactly one writer of a row's status/recency.** None of the five keeps two writers apart with
publication filters. They make the second writer *impossible*.
4. **Child -> parent rollups do exist** — but they roll up *status* or a *count*, never a label and
never a timestamp, and each is explicit and documented.
5. **The row label is a stored title derived from the user's own prompt** — never a backward scan
for the newest assistant message.
## Closest architectural match (same platform: Electron, host-service process + renderer sidebar
## that nests parent and subagent rows)
- Subagent hook events are routed to a **separate roster at ingestion, before any store write**.
The ingestion comment reads: *"Subagent activity is not the terminal's lifecycle: no chime, no
status change, no session id capture."*
- The store method recording subagent events is documented *"Never touches the parent binding's
lifecycle state."*
- The renderer **derives** status on every read inside a `useMemo` and **stores nothing**.
- The state-entry stamp is preserved server-side as
`prior !== undefined && !sessionChanged ? prior.startedAt : occurredAt`.
**There is no done-state special case.** That is the shape of Orca's *main* canonical writer
(`server-ingest-structured.ts:65`), not the renderer bridge's.
## How the references make a second writer impossible
- One reference's generic update API **structurally omits** the status/recency fields, so the type
itself forbids a second writer; a lint rule confines cache writes to owner modules.
- One consolidated *"ten independent writers across six slices"* into a single registry writer and
recorded the decision as an architecture decision record.
- One gates client updates behind a monotonic sequence check and replaces **whole rows**, so a
client cannot mutate an individual field.
## Where the references DIVERGE — do not claim precedent here
The child clock (defect C) has **no single answer**:
- one shows **no timestamp at all** on the desktop row, carrying liveness via status + derived counts;
- one shows a **live-ticking elapsed duration** that freezes at completion;
- one carries a true **advancing per-child progress field**.
All three do quarantine the spawn stamp from display. So C has several legitimate answers — choose
one with stated reasons; you may not justify it by precedent alone.
## Material deviation to disclose (repo-native terms, for the eventual PR body)
> Orca keeps two real writers of one status row apart with publication filters. Every reference
> implementation examined removes the possibility of a second writer rather than suppressing its
> traffic.
## Feasibility fact established in Orca
The producer already has the attribution and discards it:
- `src/main/claude/claude-structured-item-translation.ts:57` already parses
`parentToolUseId: claudeText(frame.parent_tool_use_id)`.
- `src/main/claude/claude-structured-journal-translation.ts:143-144` uses it only for
`observeChildActivity(...)`, then drops it; the item is journaled with no producer trace.
- The journal schemas carry **no producer field at all** — zero hits for
`parentToolUseId|agentId|parentAgentId|producer|subagentId` in
`src/shared/agent-session-journal-schemas.ts` and `src/shared/agent-session-journal-types.ts`.
- A canonical subagent id already exists next door:
`src/main/claude/claude-subagent-roster.ts:121` (`this.ids.canonical(parentToolUseId)`).
So attributing at the producer means **persisting a value we already compute and throw away three
lines later** — not plumbing a new one end to end.