Select emulator coverage from Android changes and tags, retain observed Android build and verification outcomes, and publish only proof-accepted artifacts. Keep the seven desktop assets mandatory while excluding any incomplete or unverified Android pair. Make existing native labels English, link the recorded capability follow-ups, and align inherited tests with typed delegation and UTF-8 reads. Global function-count debt remains disclosed pending the owner decision.
The Windows full-test job decodes an encoding-less read_text() as cp1252, which mangles the
middle dot of the lifecycle words in question_presentation_parity.json and fails the Python
half of the parity test. The fixture is now read with an explicit UTF-8 encoding.
Review findings (claude-opus-5 slot): `test_chat_history_paging_browser.py` still looked for the
routing receipt's Open Project button inside the note it moved out of — the assertion now names
the shared `.msg-routing-actions` row and pins its place between the note and the timestamp;
the new static check in `test_projects_v6640.py` reads `chat_activity.js` with an explicit UTF-8
encoding (the file carries a non-cp1252 glyph, and the Windows PR job has no UTF-8 mode).
Advisory: the lifecycle and answerable state lists now have one JS home in
`question_presentation.js`.
Review finding (Claude Fable 5.1 slot, critical): `_task_activity_facts` projected only
quiz_id/state/asked_at/wait_for_answer from the owner_quiz block, so the `required_question`
pointer of the 3-second activity census arrived with empty question/options, and the browser
merged those blanks over the complete history row — the Main pointer flipped between the
question and the placeholder, and a settled pointer lost its option label.
- The census projection keeps question, options, answered_index, comment and wait_ended_at
(the task result is already read and memoized; no new I/O).
- `project_question_pointer` emits question/options only when known, like the answer fields.
- The browser's pointer merge drops empty question/options/project_name from any re-delivery,
so no producer can blank a painted row (pinned in chat_decision.test.js).
- `applyQuizStateFrame` observes the live frame once; the pointer repaints from the merged
observation (advisory A1).
- The live pointer frame in `message_bus.send_quiz` is built with constant keys plus explicit
optional assignments (`test_chat_outbound_matches_message_bus_sends` forbids `**` expansion);
generated inventories regenerated after the `contracts.py` comment paydown.
Roast findings (codex gpt-6-astra) folded in:
- The pointer row carries the question, option labels, recorded answer and the
wait facts from history, the live delivery and the activity census
(`project_question_pointer`, `owner_wait_projection`; both contract mirrors),
so Main paints it from the row alone. The IntersectionObserver hydration, the
settled-source cache and the Retry state are gone: freshness is the ordinary
history reconciliation plus the `quiz_state` frame, and task detail is read
only to open the original form.
- Lifecycle observation precedence: once a live frame closed a wait, an older
history row or a detail read begun before it cannot reopen «Waiting»; an
unavailable row keeps what is known; a settled question never reopens. The
production timeout frame (`wait_for_answer:false`, no `wait_ended_at`) is the
shape the tests use.
- Parity: history attaches the task's wait record to the Project room's quiz rows
(a wait the owner resumed by ordinary input leaves no frame behind), the quiz
card reads those facts, and the parity fixture now pins the rows Python emits
against what the browser reads from them.
- Wording leads with one word — «Waiting for your answer» / «Unanswered · …» /
«You answered» / «Replaced by a newer question» / «Status unavailable» — with
three action labels; status and source lines use meta ink (DESIGN: the owner
reads them to act). The wait-ended line names the default path the task took
and that silence was not consent; a late answer's toast says where it went.
- Previews bound the option and the comment separately and never cut for less
than the marker costs.
- Module map row for `question_presentation.js`; DEVELOPMENT 11 points at the
ARCHITECTURE data flow instead of restating it; a stale comment in
`owner_quiz.py`; `contracts.py` pays its 1600-line gate down by compacting
five comment blocks.
The Main-chat pointer for a required Project question read «Question answered in
<Project>» with its `View question` button jammed against the timestamp, and the
lifecycle words did not tell the owner whether a question was waiting for him.
- One shared action-row composition, `ui_helpers.createSystemMessageActions`, now
owns space above and below the buttons, wrapping and focus-ring clearance for
question pointers, Project lifecycle rows and routing receipts; a button never
sits in a nowrap text line again.
- One lifecycle vocabulary in the pure `web/modules/question_presentation.js`,
shared by the pointer and the quiz-card header, with the Python fallback in
`project_dialogue.project_question_pointer` pinned by a shared parity fixture.
- The pointer shows the question first, then the status, the recorded option and
comment (never a locally invented answer), and the Project as its source.
- The answer POST settles the card only on a valid recorded confirmation
(`ok`, `state`, a valid index or a non-empty comment); a malformed 2xx never
substitutes the local draft (the independent audit's fabricated-answer defect).
Docs replace the touched descriptions in DESIGN §5, ARCHITECTURE 03 and
DEVELOPMENT 11. Version carriers are untouched: a contributor PR into `ouroboros`
leaves the release version to integration.
Claudexor platform gate (API keys — subscription auth NOT covered) / live · macos-latest · claude · API key only, subscription NOT covered (push) Waiting to run
Claudexor platform gate (API keys — subscription auth NOT covered) / live · ubuntu-latest · claude · API key only, subscription NOT covered (push) Waiting to run
Claudexor platform gate (API keys — subscription auth NOT covered) / live · windows-latest · claude · API key only, subscription NOT covered (push) Waiting to run
Claudexor platform gate (API keys — subscription auth NOT covered) / live · macos-latest · codex · API key only, subscription NOT covered (push) Waiting to run
Review round 1 (Fable lane): the development rule ended with a narrative of
what the retired per-execution key used to cost; the documentation contract
keeps history in git, so the sentence now stops at the measured backend fact.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
Two tests pinned the retired per-execution key: the prospective wrap-up and
the forced send still carry one identical cacheKey, and the one-shot failed
profile fact is still read without being spent by the prospective build and
consumed exactly once by the dispatch, but both now key on
`cache_key_for_model`, so the tests seed and expect that value.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
Every main-loop execution sent the Codex backend its own prompt_cache_key
(the execution id, which the Claudexor adapter also puts in the session_id
header). The backend reuses a cached prefix across conversations only under
the same key and session, so a new root task, subagent child or consciousness
cycle paid the governance prefix cold on its first round although consecutive
executions share it byte-identically (about 286k tokens between two
consciousness cycles, about 215k between two root tasks). In one measured day
those cold first rounds were 63 % of all uncached codex tokens.
`llm_claudexor.cache_key_for_model` now derives one header-safe key per data
root and model, and the three producers of the main-loop affinity use it: the
main call, the prepared and re-prepared call, and the prospective wrap-up
pricing build that must bind the same payload the send produces. Two direct
backend probes (2026-09-17) showed a second conversation under the shared key
receives 99 % of the prefix from cache, a different session_id receives none,
and a conversation's own turn state stays valid after another conversation's
turn under the shared session. API-compatible lanes keep their prefix-derived
session identity; reviewer surfaces keep their own affinities.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
A card row replayed from history now merges through the historical timeline
seam with its history id and position, so it sorts by its source position and
leaves the card when its page is released, like every other replayed timeline
item. The custody split derives the row id from the task's canonical answer
identity when the answer reaches the seam unregistered, and keeps the joined
host notice when no task can key it. The late acceptance row's head states what
the host holds (no settled verdict) and names a reviewer whose outcome is still
unknown. A placed row raises the unread badge like a standalone row did.
DESIGN §4 and ARCHITECTURE 03 name the rows that stay ordinary by design.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
The earlier reflow to hold the 1500-line band dropped two facts: a failed call
still counts in the evidence total, and the completed-event normalization takes
its label, phase and terminal truth from the canonical projector. Both are back,
paid for by the cost-checkpoint note and the child branch, which say the same in
fewer lines.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
DESIGN claimed position parity for the folded row, which a cold reload cannot
keep: the row is minted from the metrics, so it carries their time and place
while a reconnect keeps the live one. The same paragraph now says the host's
facts merge field by field, that the header's `updated` stamp follows the
turn's own narration rather than a host note or a tool call, that the row shows
its phase in ink, and that a child reads the voice rule for its own notes. The
design-system chapter keeps only the engineering obligations plus the enforcing
suites, and the transport docstring names both keyword facts its notes pass.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
The folded row's documented phases were invisible: only `error` had a colour,
so a turn mid-burst and a turn that lost a call looked like a settled one. The
row keeps its counts-only label and says the phase in ink, with the same
colour-only shape the error rule uses.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
A child's own progress frames carry the same typed `narration` fact a root's
do, but the subagent branch promoted every one of them, so a checkpoint or
fallback note inside a child's turn took over the child card's collapsed
activity line. Its non-terminal frames now follow the fact (absent stays
legacy narration), while the host's lifecycle, result and error frames keep
leading, and the collapsed line takes a child's activity only from a frame
that speaks in the turn's own voice. Two comment blocks around the branch say
the same in fewer lines, so the module stays at its 1500-line band bound.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
The folded evidence row never reaches the generic timeline upsert — the owning
frame is skipped there and `upsertToolFoldRow` passes `inPlaceByKey` itself —
so the `tools|` entry in the prefix list decided nothing.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
A review slot's run registered under the reviewed task's id was treated as
that task's delegation: the loop-exit audit listed it as open delegated
execution (Done with warnings plus a standalone System bubble) and the
periodic orphan sweep cancelled the live reviewer once the task's durable
result read completed (issue #1006).
Custody kinds: `RunCustody.review_owned` (the durable `source` under the
review substrate, consolidated from the private recovery predicate) is now
read by the orphan/kill reconcile (a review-owned row is cancelled only
behind an owner cancellation, otherwise left live with a typed reason), the
terminal custody audit, the execution-evidence counters, the nanny hold, the
crash-recovery candidate lists and the pending-invocation recovery (a review
invocation is retained, never re-posted). SETTLED rows carry `source` and
`category`. Physical custody keeps seeing every run. The consumer matrix in
tests/test_custody_owner_kinds.py is the surface a new reader joins.
Card rows: a host fact about a task is a row of that task's card. The
producer stamps `card_row` (timeline | reviews) and `card_row_id` on the chat
row (persisted by log_chat, replayed by history, mirrored in ChatOutbound).
The custody audit becomes its own typed row (`custody_notice`, its own owed
delivery id; the outbox rebuild no longer appends a host line into the
assistant answer). The late acceptance settlement stamps a host-composed
`late_settlement` note on the panel projection and its row is placed in the
card's Reviews group, which prints the note verbatim. The browser attaches a
stamped row to the task's card record as one timeline item, live and on
replay, and falls back to the standalone System row only when no card record
exists. Single-body transports keep the joined host notice text.
Docs: ARCHITECTURE 01/03/06/11, DESIGN §4/§5, DEVELOPMENT 06/11 state both
rules; generated inventories regenerated.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
The supervisor states a turn's totals more than once — the metrics event,
the terminal, a replayed summary — and each fact carries its own subset.
Replacing the whole snapshot let a later partial fact (a bare `tool_calls`)
erase the routing count that classified an addressing-only turn as a
receipt, and erase a known error count with it. Each field now keeps its
last known value until a fact actually states it, and `tool_call_counts`
counts as stated only when it is a non-empty object, so an empty map keeps
the live names instead of emptying the row behind Expand.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
Three suites pinned the shape `_emit_progress` had before the voice existed: an
owner's plain note carried no `progress_meta` at all, a transport-wait note's
meta held exactly the incident pair, and the loop's emitter fakes declared
`*, incident=None` positionally, so the round-narration call raised TypeError
instead of reaching them.
Each is updated to the fact it was actually testing. The initiator suite now
asserts the owner's frame carries only the voice, which still proves the wake
label cannot leak onto it. The transport-wait note pins `narration: False`
beside its unchanged toast pair. The loop fakes take `**_meta`, mirroring an
emitter whose optional facts a test double should not have to track.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
A turn that reads five files drew five rows, so the narration around them
scrolled out of the block. Routine execution is now ONE stationary row per
block: it counts the calls, names the tools behind Expand, and keeps the place
and the timestamp of the first frame it counted. Failures and timeouts keep
their own row where they happened and still count in the total.
The accumulator is a per-invocation state map on the record rather than a pair
of counters, so duplicate, reordered and concurrent frames all settle on the
same reading: a status never regresses, an error is counted once, and a late
start cannot reopen a finished call. One builder produces the row for both the
live frames and the host's metrics, and the meta counts follow the same
reading, so the header cannot disagree with the row while a turn runs.
The host's totals replace the derived ones only where the host stated them. An
absent field stays absent instead of reading as zero, so a terminal that
carries `tool_calls` alone can no longer turn a block that only addressed work
into content. `noteToolMetrics` drops its per-call-row guard and always upserts
the same key; the keyed upsert makes a second row impossible.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
Owner decision 16.09 (Q1 = A): host-authored notes stay visible timeline rows
but never become the card title or the collapsed activity line. Only the model's
narration does.
The progress projection reads the typed `narration` fact instead of promoting
every progress frame: a host note keeps its row, its phase and its markdown, and
loses only `promote`/`human`, which are exactly what feed the title and the
collapsed line. A frame without the key is a legacy frame and is promoted as
before, so older workers, supervisor-authored notes and stored rows are
unaffected. The progress reconstruction forwards the key from the history row,
so the replay of a turn behaves like the live turn did.
A turn whose progress was host notes only therefore keeps its coined name (or
the running placeholder) and shows an empty activity line.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
A progress frame carries the model's own round narration and the host's notes
about the turn (checkpoints, fallback, plan, acceptance, nudge, transport,
density) under one type, so the only way a reader could tell them apart was to
match the note's wording, which BIBLE P5 forbids.
The worker now stamps the fact instead. `_emit_progress` takes `narration` and
writes `progress_meta.narration` on EVERY frame it emits, so absence means "an
older worker or a row written before the fact existed" rather than "a host
note". `_emit_round_progress` is the single producer that passes True, for both
of its emissions; the loop-level notifier and the whole ToolContext ABI
(`emit_progress_fn`, one positional argument) keep the default.
The key needs no transport work: the delivery seam already spreads progress_meta
onto the top level of the live frame and of the stored progress row. It joins
the ChatOutbound contract in both mirrors and the progress-meta whitelist, so a
reload replays the same voice the live frame carried. `cancelable`'s comment is
reflowed, without changing a word, to keep contracts.py at its 1600-line cap.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
DESIGN "Conversation activity block" states the rule the chat block follows:
the title, collapsed line and timeline carry the model's narration (typed
`narration` fact) and what needs the owner's eyes; host notes stay visible
rows that never claim the title; successful tool calls fold into one evidence
row per block built by chat_activity.js::toolEvidenceView from the observed
call identities and, once present, the host metrics; a per-event UI is judged
at a realistic burst size. Architecture chapter 03 carries the fact flow
(producer, live frame, history whitelist, replay, evidence row) and the
design-system chapter the engineering obligations.
Claudexor platform gate (API keys — subscription auth NOT covered) / live · macos-latest · claude · API key only, subscription NOT covered (push) Waiting to run
Claudexor platform gate (API keys — subscription auth NOT covered) / live · ubuntu-latest · claude · API key only, subscription NOT covered (push) Waiting to run
Claudexor platform gate (API keys — subscription auth NOT covered) / live · windows-latest · claude · API key only, subscription NOT covered (push) Waiting to run
Claudexor platform gate (API keys — subscription auth NOT covered) / live · macos-latest · codex · API key only, subscription NOT covered (push) Waiting to run
The full battery on 3ac880fd9 failed one test: the JS effort list is a
hand-maintained mirror of config.EFFORT_SCALE and its guard reads the
`{ value: '<tier>' }` entries inside the EFFORT_OPTIONS literal; the folded
pair table hid them. The literal is back, two entries per line, so
settings_ui.js stays under the 1000-line band.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
Owner decision (16.09, 1=A): an empty OUROBOROS_EFFORT_CONSCIOUSNESS slot now
means the Task / Chat effort, the same way an empty model slot means Main — a
wake-up is an ordinary Main turn and shares its request shape. A set value is
honored; an invalid one is treated as empty. The settings default becomes empty,
the Consciousness segmented field gains a "Same as Task / Chat" option, and the
configuration doc row says so.
Semantic merge fix for PR #970: its static UI contract pinned blockHasWork on
the shouldAlwaysShowTaskCard helper that this branch retired with the
bg-consciousness card kind; the fixture now pins the retired form (the
quick-test failure on 505f6f4ae).
The effort option table folds onto two lines so settings_ui.js stays under the
1000-line ratchet band.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
Conflicts: supervisor/log_addressing.py and supervisor/worker_chat_lane.py (the turn
event queue carries both the initiator label and the lazy-naming callback),
web/modules/chat.js and model_wait.js (the target's chrome-follows-the-work model
wins; no lane placeholder survives), tests/test_log_forwarding.py (both blocks),
docs/architecture/03 (their chrome paragraph plus the consciousness wording),
docs/DOMAIN_MAP.md and the v7next inventories (regenerated). The merged get_tools
sits at the 300-line ratchet cap (one entry joined onto one line).
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
The full host UI smoke lane on the merged head (workflow_dispatch; never run on
pull requests) showed three failures caused by #970. One was a real regression:
the viewport-preservation anchor picks a boundary inside a live card that
crosses the top of the viewport, and a wait-only block now carries no title
placeholder and no conversion button, so nothing inside it was anchorable and
the capture fell back to the card's own top — a wait update that shrank the
block moved the reader's messages by 40 px. Such a card now anchors on the first
message that follows it, which is what the reader is looking at (unit pin in
render_batch.test.js; the model-wait browser test passes again).
The other two were pins of text the sprint changed on purpose: the `Reason:`
label in front of a cause sentence and the answer excerpt in the Main
completion row. Both tests now pin the shipped shape. The remaining 34 lane
failures predate #970 and are filed as their own issue.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
- INTEGRATE_CAPPED_TREE joins the classifier's current-producer contracts as an
integration_blocked outcome.
- set_next_wakeup's receipt and schema say the interval is finish-relative: it applies
from the end of the next wake-up, a pending wake-up keeps its time (astra scope,
round 7).
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
A capped tree (an Act/Observe consciousness tree) is refused a self_worktree child and a
system-repo patch integration regardless of the install's runtime mode or the owner's
mutative-subagent toggle: the cap is the level's, not the install's. The round-4 gate had
excluded installs already running light, where an explicit toggle admits self_worktree
children (astra scope, round 6).
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
windows-latest full-test on #970 (and #963 before it) failed in
test_heartbeat_continues_during_extension_reconcile with PermissionError on
review_job.json: the poll opened the file while the heartbeat's atomic replace
was in flight. The writer retries exactly that race (utils.replace_atomic); the
reader now does the mirror image — one read on POSIX, a bounded retry on
PermissionError on Windows — and still fails loudly if the file stays locked.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
- Observe does without skill_exec and run_ci_tests (an enabled skill's script; a branch
push plus a workflow dispatch) — the two remaining built-in execution verbs.
- A refused wake launch debounces the next event like a skip does, so the backoff it
armed is never undone on the next supervisor pass.
- The direct lane names the card's chat in the closed-bound frame; reconciling a replayed
row into an existing card projects the closed bound too (a missed timeout frame).
- Docs: PERSISTENCE drops the retired observation fold; architecture/06 names the two
round-4 gates the mode cap binds at; DESIGN describes the question pointer's states.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>