Skip to content

feat: make loop runs legible and task lists calm - #452

Merged
pedronauck merged 27 commits into
mainfrom
loop-legibility
Aug 22, 2026
Merged

pedronauck merged 27 commits into
mainfrom
loop-legibility

Conversation

@pedronauck

Copy link
Copy Markdown
Member

What & why

Loops run through the autonomy kernel: every run creates one coordinator record plus one execution record per action node × fan-out item × round. That design is fine. What wasn't fine is that those records were the only thing anyone could see, and they were everywhere.

Three concrete failures, all confirmed on the owner's dev daemon before this branch started:

  1. Task lists drowned. A single active loop added dozens of near-identical rows full of machine ids to every task listing — web list/board/dashboard/inbox, compozy task list, the HTTP/UDS catalog, and the compozy__task_list native tool. The client-side nesting that was supposed to contain this degraded back to a flood as soon as pagination kicked in.
  2. The run page underserved. It was an operator cockpit — competing panels, exception-only node rows, machine ids in primary positions — that still lacked the operator's single most useful view: the graph that is actually executing. docs/qa/bugs/BUG-20260719-autonomous-progress-unobservable.md (P1, independently re-found 6 times) documents operators concluding "stalled" while real work was happening.
  3. Terminal runs leaked live records. Two runs of revisao-paralela crashed at ~17:03 on 2026-08-19. Fifteen minutes later compozy task list still showed both coordinators QUEUED with cells pending — permanently. Settlement only ever ran on the happy path (global_db_task_coordinator_settlement.go); nothing swept the other way.

This branch fixes legibility and lifecycle integrity without touching the execution architecture. No second executor, no new queue, no new run or node lifecycle states. Loop records get classified at the source and filtered out of task listings by default; the Loops area becomes the complete two-register observability home; and every terminal transition settles its records, with an idempotent sweep that repairs the orphans that already exist.

Spec, ADRs and task breakdown: .compozy/tasks/loop-task-legibility/ (7 tasks, _spec.md, ADR-001..006).


Implementation by surface

Runtime — internal/loop

Three computed projections over tables that already exist. No materialized state, so there is no second copy to reconcile.

  • roster.go, roster_projection.go, roster_rollup.go, roster_routes.go, roster_cursor.go — the node × generation roster. State comes from pinned topology plus durable outputs, controls, waits, attempts and route evidence. Fan-out arrives as derived rollup counts so a wide run never has to page every item.
  • briefing.go, run_read_briefing.go — the briefing is a pure projection of the roster. Tone resolves by first match: approval > quarantine > request > failure > quota/backoff > running > terminal.
  • timeline.go — durable, paged, resumable history over loop_run_events. Replaces the client-window-only story, so full history survives a reload and a long run.
  • settlement.go, reconciler.go — the terminal settlement rule and the sweep.
  • run_read_service.go, run_read.go, run_summary.go — assembly and the service interface.

Long histories stay SQL-backed without N+1 reads: timeline and route evidence scan fixed-head backward batches, run ordering happens before keyset pagination, and attention/progress/fork summaries use constant-count batch queries.

Persistence — internal/store/globaldb

  • Settlement helpers made authoritative on every terminal path, not just natural completion.
  • Migration 00081_schema.sql: adds nullable output_id and artifact_name to loop_generation_outputs. Append-only; atlas.sum and the declarative source (schema/definitions/50_loops.sql) regenerated through make codegen.
  • Two ownership bugs found and fixed during QA: a daemon-owned coordinator lease with no session_id failed its SQL ownership fence because the predicate treated two NULLs as unequal, and a coordinator that reached needs_attention through ordinary retry exhaustion blocked reconciliation. Both were reproduced red first, then fixed with focused store regressions.

Daemon — internal/daemon

  • loop_reconciler_boot.go — one sweep at boot, always, regardless of interval. This is what retroactively repairs pre-existing orphans.
  • loop_reconciler_runtime.go — the periodic ticker.
  • loop_api_reads.go, loop_api_run_list.go — read wiring. internal/daemon remains the only composition root; mage Boundaries is unchanged.

Repaired records are stamped reason: "reconciled_run_terminal", which is deliberately distinguishable from natural settlement's loop_run_terminal. The sweep is idempotent and never touches an active run.

HTTP / UDS — internal/api

Three new routes, identical on both transports:

GET /api/workspaces/{workspace_id}/loop-runs/{run_id}/nodes
GET /api/workspaces/{workspace_id}/loop-runs/{run_id}/briefing
GET /api/workspaces/{workspace_id}/loop-runs/{run_id}/timeline

GET /api/tasks gains two typed query fields: include_loop (bool, default false) and loop_run_id (string, implies include). Both are typed fields, never inferred from query text. Catalog items gain an optional loop object carrying run id, loop name, role, generation and item index — which is what lets the web delete its id-string parsing entirely. The single-task read carries the same object, so a deep link into a loop record never depends on list navigation.

The timeline's first page is the newest window and always returns head_seq. Older history pages backward on demand through an opaque cursor bound to {run, view, snapshot head, before}, so the page set can't shift under concurrent appends. Replaying a cursor against another run — or against a fork, which mints a new run id — returns 409 timeline_branch_changed rather than splicing two histories together. The existing SSE stream is unchanged; live readers take one first page then attach with after_sequence=head_seq, and the seam de-dupes by sequence.

openapi/compozy.json and the generated TS types are regenerated; no route was removed.

CLI — internal/cli

Three new verbs, wired in loop.go:

  • compozy loop why <run> — the same verdict the run page leads with, as one plain sentence plus the exact unblock command. Healthy and terminal runs answer too; it never returns an empty object. Terminal briefings carry outcome and artifacts as typed fields.
  • compozy loop nodes --run <run> --all — the complete node × round roster, healthy nodes included. Workspace scope keeps its four exception states; --state only gains running/queued/succeeded/all when --run is set.
  • compozy loop events <run> --after <seq> --follow — durable timeline plus follow. --view notable by default, --view all for the ordered union. Heartbeat-class chatter coalesces into one ×N entry whose seq is the last raw sequence it spans, so resuming after it never replays folded events. --follow exits 0 cleanly when the run goes terminal.

compozy task list gains --include-loop and --loop-run <id> (the latter implies the former). --parent <task-id> keeps returning children with no flag, because explicit parentage is already an explicit request.

compozy loop runs is now server-ordered end to end: needs-you first, then active, then terminal — before pagination, so the ordering holds across pages.

Native tools & extensions

  • compozy__task_list gains include_loop and loop_run_id (internal/tools/builtin/task_list_schema.go); the catalog digest in testdata/native-tool-catalog.json is regenerated.
  • The extension host API task-query surface carries the same fields (internal/extension/host_api_task_query.go, host_api.go, host_api_options.go), so bridges and extensions reading task listings inherit the calm default with no bespoke carve-out.

Web — web/src/systems/{tasks,loops}

Tasks (calm default). The id-string regex and the client-side nesting are deleted, not patched. Loop records are excluded by default on every mode; a quiet reveal filter brings them back, visually distinguished and always linked to their run. Dashboard and inbox lanes stop counting them. Provenance is read from the structured loop object.

Run page (two registers, one page, no mode toggle). The default register answers the 30-second briefing test in plain words: what's running, what needs you, how far along, what it produced — no machine ids in primary positions. Needs-you and failure never collapse under disclosure. New components include loop-run-briefing.tsx, loop-run-steps-progress.tsx, loop-run-story.tsx, loop-run-artifact-list.tsx, loop-node-state-chip.tsx.

The Inspect register sits one step deeper: loop-run-dag.tsx (the live authored graph, the largest redesign target here), loop-node-roster.tsx, loop-node-panel.tsx, loop-generation-history.tsx, loop-run-events-lane.tsx.

Runs roster re-ranks to lead with needs-you and plain outcomes, and renders a degraded-transport state honestly instead of pretending to be live.

Docs & official skill

  • packages/site/content/docs/cli/loop/{why,nodes,events,runs,respond,approve,index}.mdx, cli/task/list.mdx, configuration/config-toml.mdx, loops/{running,configure,human-requests}.mdx.
  • skills/compozy/references/{loops,configuration,tasks-and-orchestration}.md.

Behavioral contracts

  • Classification is by provenance, never by name or id-string matching. A person's task titled "loop something" is a work item. A loop-created record is an execution record. Always.
  • Exclusion is default-filtering, never erasure. Hidden records stay queryable, linkable and visually distinguished when revealed. No surface drops them from existence.
  • Default parity across all four transports. Web, CLI, HTTP/UDS and native tool apply the same default and honor the same include filter. No surface tells a different story.
  • Aggregates follow the default. Counts, facets, dashboards and inbox lanes never include hidden loop records.
  • Escalations never get lost. A quarantined or attention-flagged cell still surfaces through the loop attention lane. Leaving Tasks reduces visibility nowhere.
  • Terminal settlement invariant. A run in any terminal outcome has zero live execution records. Crash-window divergence converges within one reconciliation cycle. The sweep is idempotent and audit-stamped with a reason distinct from natural completion, and never touches active runs.
  • Current-state truth. A node that recovered on a later attempt never reads by its stale failure, and a transient failure the run absorbed never paints the run headline as failed. Attempt history stays fully readable in Inspect — recovery hides nothing, it just wins the headline.
  • Progress semantics. Progress is settled action steps out of total action steps in the current round, plus a round counter that stays hidden on round 1. Attempts are step metadata, never sibling steps. Route-not-taken and never-materialized branches are excluded from totals and render neutrally with no fabricated timestamps.
  • Truthful UI (SD-007). Every rendered state maps to a runtime-modeled state. Rollups are derived, never entered.
  • Workspace scoping. Every read and stream is workspace-scoped. A cross-workspace run id returns 404 loop_run_not_found — scoping, never a leak.
  • Deterministic errors. 404 loop_run_not_found, 400 invalid_node_state (with the allowed list), 400 invalid_cursor, 400 invalid_query_field, 409 timeline_branch_changed, plus CLI exit codes 1 and 2. Full table in _dx.md.

Compozy Impact Audit

  • Native tools: compozy__task_list gains typed include_loop and loop_run_id arguments and the optional loop provenance object on items; default behavior (argument absent) excludes loop records, matching every other surface. Schema in internal/tools/builtin/task_list_schema.go; loop toolset descriptors touched in loops.go and loops_output_schemas.go; the catalog digest fixture testdata/native-tool-catalog.json is regenerated and asserted by builtin_test.go. No tool was added, renamed or removed; no risk flag or capability gate changed.
  • Extensibility and hooks: the extension host API task-query surface carries the same two typed fields (internal/extension/host_api_task_query.go, host_api.go, host_api_options.go, contract/sdk_enums.go), so extensions and bridges reading task listings inherit the calm default rather than special-casing it. Loop lifecycle hook bridges are extended for the new terminal/started payloads (internal/daemon/hooks_bridge_loop_{started,terminal}.go, hooks_bridge_watch_events.go). No new extension point was added — the spec explicitly predicted none and none was needed. Config lifecycle: one new key, covered below. MCP sidecars, skills/capabilities registries and resource registries: unchanged, and checked.
  • Workspace data isolation: every datum this branch introduces is workspace-scoped. The three new read routes take workspace_id in the path and resolve the run inside that scope; a run id from another workspace returns 404 loop_run_not_found rather than a 403 that would confirm existence. The roster, briefing and timeline queries all carry the workspace predicate into SQL rather than filtering after the fetch. The new loop object on catalog items is derived from rows already scoped by the catalog query. The existing per-run SSE stream is unchanged and keeps its workspace scoping. Web caches key on workspace, and a QA-found data-boundary defect here was fixed and locked: palette catalog carry-forward is now limited to the same workspace's null-client → attached-client transition, with a canonical hook regression proving workspace A's commands never render while workspace B is pending. LP-web-* scenarios and internal/api handler tests cover cross-workspace refusal.
  • Official Compozy skill: updated, because public behavior changed. skills/compozy/references/loops.md documents loop why, loop nodes --run --all, loop events --follow/--after/--view and the settlement/audit reads; references/tasks-and-orchestration.md documents the calm task-list default and the --include-loop / --loop-run escape hatch; references/configuration.md documents [loops] reconcile_interval.

Migrations & config lifecycle

Migration. One: internal/store/globaldb/schema/migrations/00081_schema.sql, adding two nullable columns to loop_generation_outputs:

ALTER TABLE `loop_generation_outputs` ADD COLUMN `output_id` text NULL;
ALTER TABLE `loop_generation_outputs` ADD COLUMN `artifact_name` text NULL;

Append-only: no existing migration's bytes, version, or order changed. atlas.sum extended, schema/definitions/50_loops.sql updated as the declarative source, sqlc output regenerated via make codegen. Both columns are nullable, so the change is additive for existing databases and needs no backfill.

Config. One new key on the existing [loops] section:

[loops]
# How often the daemon sweeps for terminal runs that still have live execution records.
# Settlement runs inline on every terminal transition; this is the safety net.
reconcile_interval = "1m"
  • Default "1m" (internal/config/loops.go).
  • 0s and negatives are rejected at validation with reconcile_interval must be positive; an invalid value preserves the prior config rather than half-applying.
  • Classified RestartRequired / DiffClassRestartRequired in internal/config/lifecycle, with the classification asserted in lifecycle_test.go.
  • Boot always runs one sweep regardless of the interval, so orphan repair does not wait for the first tick.
  • Documented in packages/site/content/docs/configuration/config-toml.mdx and the official skill.

How you verified it

Deep review

One deep-review round was run before the QA tail. It found 221 issues; all 221 were resolved — the remediation landed in 36ee47f1 fix: remediate loop legibility review findings plus the follow-up fix commits on this branch. state.yaml records rounds: 1, last_verdict: SHIP.

QA execution

Full report: docs/qa/reports/2026-08-21-loop-task-legibility.md. Run in fresh isolated labs with unique COMPOZY_HOME, daemon ports and sockets.

Lane Result
Web E2E (daemon-served, fresh lab) 227 passed · 3 declared conditional skips · 0 failed (36.0 min)
Daemon runtime 173 / 173
HTTP API 15 / 15
UDS API 44 / 44
Runtime harness 8 / 8
CLI 4 / 4
Visual Contracts 42 / 42 reviewed bundles, 0 blocking divergences

Before that full web lane went green, 23 deterministic failures across Dashboard, OS shell, Settings, Storybook and Worktrees were classified and repaired. Remediation used focused reruns only — no timeout was extended and no expectation was weakened. Where a strict assertion had been softened, it was restored: the loop state-chip accessibility check went back to toHaveAccessibleName(/\S/) and passed in the complete lane.

Scenario walks: all 13 planned tracker walks in docs/qa/scenarios/ ran only after the complete lane was green, plus three overlapping regression scenarios for gate provenance, pending-request provenance, and cancellation/duration truth. All pass.

Settlement was verified against a real seeded crash boundary rather than a unit fixture: looprun-c6fc681a5171ff54 was left terminal canceled with three unsettled execution records. The boot sweep reported runs_examined=1, records_settled=3, orphans_repaired=1; fresh public reads showed the coordinator and both cells canceled with reconciled_run_terminal on the coordinator timeline. A second boot reported zero examined, zero settled, zero repaired, and the repair event count stayed at 1 — the idempotency claim, checked rather than asserted.

QA lab teardown is clean on every terminal path: qa/teardown.json records "clean": true with an empty survivor list, and ports 62112, 6006 and 6008 have no listener.

make gate-full

Stated plainly, because it matters for how you read the rest of this section:

make gate-full was attempted locally several times. Earlier attempts surfaced deterministic failures — formatting, React lint, Go lint, cursor, and settlement-authority issues — and every one of those was fixed and verified. Later full attempts were stopped or failed on machine-level timeouts, after the relevant focused race suites and the exact full Go lint command had already passed with 0 issues. At that point the maintainer explicitly waived another complete local run and delegated the final full gate to PR CI.

So: no local make gate-full PASS is being claimed here. CI owns that verdict on this PR.


Screenshots

All six are the implementation.png captures from the Visual Contract bundles under .compozy/tasks/loop-task-legibility/evidence/visual/, taken from Storybook stories at 1440×900. They are the implementation half of a reference/implementation parity bundle — not live daemon captures. Every bundle shown here carries verdict: PASS with blocking_divergences: 0.

Run page, default read — healthy running register (task_05/VC-01, story looprunpage--register-running). Status in plain words, steps and round progress, the story, the usage rail. No machine ids in primary positions.

Run page default read

Tasks, loop records revealed — the mixed list with the reveal filter on (task_04/VC-02, story taskslistsurface--revealed-loop-records). This is the opt-in view; the default has none of these rows. Revealed records are visually distinguished and linked back to their run.

Tasks list with loop records revealed

Inspect register, live run graph — the authored DAG rendering live (task_05/VC-17, story looprunregisters--graph-live). This is the view the P1 observability bug was about.

Inspect register live run graph

Inspect register, node panel — one node opened: attempts, links to its session and execution record, and the verbs valid for its current state (task_05/VC-23, story looprunregisters--node-panel-open).

Inspect register node panel

Inspect register, complete node roster — every authored node × round, healthy ones included, not just exceptions (task_05/VC-25, story looprunregisters--roster-complete).

Inspect register node roster

Runs roster — needs-you runs ordered first, server-side, before pagination (task_05/VC-33, story loopruns--default).

Runs roster with needs-you first


Rollout & risk

Size is the main risk. 521 files, +44,584 / −12,674, across runtime, store, daemon, API, CLI, web and docs. It is one program rather than one change, and it is best read task by task through .compozy/tasks/loop-task-legibility/task_01..07.md. The mitigation is coverage: 42 visual contracts, a full daemon-served E2E lane, 13 scenario walks and the runtime lanes above.

What is additive and low-risk:

  • The three new read routes add no writes and remove nothing.
  • Migration 00081 adds two nullable columns; existing rows need no backfill.
  • [loops] reconcile_interval has a working default and rejects invalid values without partially applying.
  • The reconciliation sweep only examines runs already in a terminal outcome, is idempotent across boots, and stamps a distinguishable reason. Active runs are untouched.

What changes behavior visibly, on purpose:

  • Task listings stop showing loop execution records by default on all four transports. Anything scripted against the old behavior needs --include-loop / include_loop=true. This is the point of the change, and it is documented in the CLI reference, the config docs and the official skill.
  • The run page layout changes substantially. Nothing is removed — operator depth moved one disclosure step deeper into Inspect.

Escalation path if the sweep misbehaves: set [loops] reconcile_interval to a longer cadence and restart; boot-time repair still runs once, and inline settlement on terminal transitions is independent of the ticker.

Not merged, not gated locally. PR CI owns the full monorepo gate for this branch — see the make gate-full note above.


  • make gate passes locally — not claimed; see the make gate-full section. Focused race suites and the full Go lint command passed with 0 issues; the complete gate is delegated to CI.
  • New and changed behavior is covered by tests — runtime lanes, 227-test daemon-served web E2E lane, 42 visual contracts, 13 scenario walks.
  • Agent-written: implemented across a cy-loop-tasks run and reviewed by the maintainer, who ran the focused race and lint checks and made the CI delegation call.

pedronauck and others added 26 commits August 21, 2026 23:45
Checkpoint via cy-loop-tasks (iteration 2, phase B mode=tasks).

Co-Authored-By: Codex <[email protected]>
Checkpoint via cy-loop-tasks (iteration 4, phase B mode=tasks).

Co-Authored-By: Codex <[email protected]>
Visual Contract row VC-08 was recorded as PASS against a capture that showed
a terminal "Done" run with no partial signal at all. The story it targets
staged no briefing read, and buildScenarioProps filled the gap with a
manufactured verdict - tone "ok", a headline built from the loop name and
round, no outcome, no artifacts - so the capture photographed a state nobody
had staged. VC-03, VC-06 and VC-07 failed the same way inside their recorded
scope boundaries.

Make the omission impossible instead of papering over it: briefing is now
required on LoopRunStoryScenario, the placeholder is deleted, and briefingFor
copies the run's server-owned run_id, status, progress and usage while
requiring each scenario to state the tone and headline the daemon writes. The
compiler enumerates every site, so a scenario that has not staged a verdict
cannot render one.

Add registerPartialOutputsScenario for VC-08: a run that failed after seven of
ten fix workers returned, labelling its output partial in the default register
through the artifact's warning note, the join's partial chip and the fan-out's
"partial 7 of 10" coverage - all server-owned reads, none derived client-side.

Split the metric-ratchet scenarios and the durable lifecycle payloads into
their own files to stay under the 500-line cap.

Co-Authored-By: Claude Opus 5 <[email protected]>
The visual-contract evidence for task_04/task_05 was passing on captures that
did not show their named state. The fixture layer was the reason, and it is
what this fixes.

`briefingFor` documented that a scenario may not overwrite the server-owned
briefing fields, but `StoryVerdict` admitted all of them and `...verdict` was
spread last, so only the comment stood between a story and a briefing that
contradicted the run beside it. The type now excludes `run_id`/`status`/
`progress`/`usage`, the derived fields land after the caller's spread, and the
two usage facts the record cannot supply — `cost_usd`, `duration` — are kept as
a named optional. `makeBriefing` stopped being a second staging path: every
register and visual-contract scenario derives its briefing from its own run, so
the captures no longer photograph 82.4k tokens over a run recording 68k, a
"Done" strip over a failed run, or a calm verdict over a cancelled one.

Beat titles are the daemon's own (`Step <node> running`, `Round 2 started`)
rather than invented fixture copy, and the long story stages the fold the
daemon serves, so the paging row shows a coalesced beat and the way back
instead of a wall of identical rows. The story clock is stamped once per load
rather than pinned to a literal instant: projections took the pinned value and
components render against the wall clock, so every beat read "1d ago" and a
retry two minutes in the future read "next 1d ago".

Three surfaces were understating what the product does, and one was leaking
wire vocabulary into the default read:

- the needs-you card printed `needs_approval · <gate id>`, and
  `on_exceeded: <enum>` on the budget gate — two enums and a machine id in the
  register task_05 requirement 1 keeps them out of. It now names the asking
  gate in words with its round, and leaves the budget policy to the sentence
  the usage rail already carries.
- the request card never said who was asking, though `node_id` and
  `generation` were on the wire the whole time.
- a degraded runs roster said the rows were the last read without saying how
  old it was, which reads as "this is current".
- the roster answered "not started" about steps that had plainly run. A step
  whose timing did not survive now reads `unknown`.

Stories now stage what the host wires: Compare/Fork on the generation history,
`Browse loops` on the empty roster, an `attention` summary so the roster's
needs-you group exists, and reduced motion through `MotionConfig` rather than a
Storybook global that moved neither the context nor the media query. VC-22 gets
a deep graph of its own instead of re-staging VC-21's wide one.

Co-Authored-By: Claude Opus 5 <[email protected]>
Inspecting the remaining twenty-one visual-contract rows one image at a time
turned up three more states the page was not actually in, plus a test that
asserted a number instead of an invariant.

The story pane reads the durable timeline, and several scenarios staged their
events richly while staging no timeline at all — so a run four rounds deep with
21k tokens spent rendered "Nothing has happened in this run yet." and four
contract rows captured that empty pane as their evidence. The read is now
derived from the scenario's own events, mirroring what the daemon does with
them at read time; kinds the mapping does not know are skipped rather than
given an invented sentence.

The terminal graph was drawing a finished run as though it were still on its
way: the scenario staged roster rows for two earlier rounds only, so the round
the graph draws had almost none and every unlisted step read "Reachable.
Nothing has reached it yet." The terminal round is staged in full now, and the
branch the run declined reads as declined.

Cancellation cause and actor move onto the roster row. Strategy-canceled and
operator-canceled share the neutral ramp by design, so the state word alone
cannot tell them apart — the roster showed two identical grey `canceled` chips
and the distinction US-012.EC-2 asks for was unreadable until the node panel
was opened. The node panel's builder is shared rather than copied, so the
runtime's own reason codes stay suppressed in both places.

`expect(scenarios.length).toBeGreaterThanOrEqual(18)` was a structural
threshold pretending to be a guard: the invariant is that a staged briefing
agrees with its run, and freezing a fixture count would fail on a scenario
being added or retired, neither of which can break that. It is now a plain
non-vacuity check, and the sweep reaches every module that exports scenarios
instead of two.

Co-Authored-By: Claude Opus 5 <[email protected]>
VC-35 stages "dozens active", and the reference note is specific about where the
scale goes: "Scale changes the count, not the composition. The needs-you group
still sorts first and stays small; active collapses into one scrollable table."

The story built its thirty runs by cycling every seed of `loopRunFixtures`. That
list is mostly terminal runs, so thirty drawn from it in order inherited the mix
two-fold and the roster sorted them into Needs you 2, Active 8, Recent 20 — the
dozens landed in Recent, the one group the row exists to show held eight, and
the needs-you group was twice the size the note calls small. The capture was
passing on a state it did not stage.

The scale fixture moves into its own module and partitions the seeds by the same
two facts `groupOf` groups on — a served `attention` summary, and whether the
status is terminal — rather than by naming ids. Thirty runs still, now one
needs-you, twenty-six active and a settled tail of three, so the composition
still reads as three groups while the count lands where the contract puts it.
Reusing the roster's own predicate means a change to the grouping rule carries
this fixture with it instead of silently dropping the bulk into the wrong group
again, which is how the defect survived the first pass.

The canonical `LoopRunsView` suite gains the invariant: at scale the three
groups keep their order, Active holds the dozens, Needs you stays small, every
run is accounted for, and no KPI band appears.

Co-Authored-By: Claude Opus 5 <[email protected]>
Checkpoint via cy-loop-tasks (iteration 8, phase B mode=tasks).

Co-Authored-By: Codex <[email protected]>
@greptile-apps

greptile-apps Bot commented Aug 22, 2026

Copy link
Copy Markdown

Too many files changed for review (521 files, 100 file limit).

@vercel

vercel Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
compozy-site Ready Ready Preview Aug 22, 2026 8:43pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 420 files, which is 120 over the limit of 300.

To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e7307082-ed85-4769-95b9-4be9a50402e3

📥 Commits

Reviewing files that changed from the base of the PR and between 609f2d2 and 5fcc3c9.

⛔ Files ignored due to path filters (101)
  • .agents/skills/eng/eng-real-scenario-qa/SKILL.md is excluded by !**/*.md, !.agents/**
  • .agents/skills/eng/eng-real-scenario-qa/scripts/observe-runtime.py is excluded by !.agents/**
  • .agents/skills/eng/eng-real-scenario-qa/scripts/test_observe_runtime.py is excluded by !.agents/**
  • config.toml is excluded by !**/*.toml
  • docs/qa/automation-backlog/runtime-owned-progress-observer.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260719-autonomous-progress-unobservable.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260821-coordinator-lease-exhausted.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260821-loop-briefing-empty-output-labels.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260821-loop-respond-cli-reference-drift.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260821-loop-timeline-head-omitted.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260821-loop-unblocker-invalid-json.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260821-sessionless-lease-recovery.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-approval-hides-sibling-request.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-coordinator-progress-publication-event-shortfall.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-empty-briefing-artifacts-null.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-fanout-partial-rollup-queued.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-fanout-rollup-container-identity.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-home-overview-error-copy-drift.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-loop-respond-stdin-e2e-drift.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-loop-run-playwright-fixture-drift.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-loop-task-deep-link-dotted-id-404.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-needs-you-progress-seed-has-no-action.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-requeued-continuation-epoch-drift.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-retained-continuation-coordinator-selection.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-retained-loop-task-seed-timestamp.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-tasks-empty-shell-hides-loop-reveal.md is excluded by !**/*.md
  • docs/qa/bugs/BUG-20260822-tasks-loop-e2e-workspace-leak.md is excluded by !**/*.md
  • docs/qa/charters/CH-loop-legibility-authoring-run-canary.md is excluded by !**/*.md
  • docs/qa/charters/CH-loop-legibility-calm-catalog-parity.md is excluded by !**/*.md
  • docs/qa/charters/CH-loop-legibility-operator-register.md is excluded by !**/*.md
  • docs/qa/charters/CH-loop-legibility-run-default-read.md is excluded by !**/*.md
  • docs/qa/charters/CH-loop-legibility-run-read-resume.md is excluded by !**/*.md
  • docs/qa/charters/CH-loop-legibility-settlement-repair.md is excluded by !**/*.md
  • docs/qa/charters/CH-loop-legibility-tasks-calm-web.md is excluded by !**/*.md
  • docs/qa/journeys/J-diagnose-loop-run-operator.md is excluded by !**/*.md
  • docs/qa/journeys/J-loop-terminal-recovery.md is excluded by !**/*.md
  • docs/qa/journeys/J-operate-loop-run-headless.md is excluded by !**/*.md
  • docs/qa/journeys/J-supervise-loop-steady-state.md is excluded by !**/*.md
  • docs/qa/reports/2026-08-21-loop-task-legibility.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-fanout-progress-naming.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-loop-lifecycle-config-cli.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-loop-run-deep-link.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-run-detail-story-redesign.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-run-read-agent-journey.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-runs-roster-server-ordering.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-terminal-loop-settlement.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-attention-loop-rows.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-detail-inventory-contract.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-needs-you-gate-provenance.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-pending-request-provenance.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-roster-unknown-duration-cancellation.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-run-default-read-briefing.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-run-operator-register.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-runs-roster-rerank.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-strategy-progress.md is excluded by !**/*.md
  • docs/qa/scenarios/LP-web-timeline-graph-rows.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-073.md is excluded by !**/*.md
  • docs/qa/scenarios/TA-task-list-calm-loop-default.md is excluded by !**/*.md
  • docs/qa/scenarios/TA-web-task-detail-loop-provenance.md is excluded by !**/*.md
  • docs/qa/scenarios/TA-web-task-list-loop-subtask-nesting.md is excluded by !**/*.md
  • docs/qa/scenarios/TA-web-tasks-calm-default-reveal.md is excluded by !**/*.md
  • internal/store/globaldb/schema/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
  • internal/testutil/acpmock/testdata/loop_node_lifecycle_fixture.json is excluded by !**/*.json
  • internal/tools/builtin/testdata/native-tool-catalog.json is excluded by !**/*.json
  • openapi/compozy.json is excluded by !**/*.json
  • packages/site/content/docs/cli/loop/approve.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/loop/events.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/loop/index.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/loop/meta.json is excluded by !**/*.json
  • packages/site/content/docs/cli/loop/nodes.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/loop/respond.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/loop/runs.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/loop/why.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/task/list.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/configuration/config-toml.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/loops/configure.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/loops/human-requests.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/loops/running.mdx is excluded by !**/*.mdx
  • sdk/typescript/src/generated/contracts.ts is excluded by !**/generated/**, !**/generated/**
  • skills/compozy/references/configuration.md is excluded by !**/*.md
  • skills/compozy/references/loops.md is excluded by !**/*.md
  • skills/compozy/references/tasks-and-orchestration.md is excluded by !**/*.md
  • web/e2e/__tests__/loop-run.spec.ts is excluded by !web/e2e/**
  • web/e2e/__tests__/loops-graph-eng.spec.ts is excluded by !web/e2e/**
  • web/e2e/__tests__/loops.spec.ts is excluded by !web/e2e/**
  • web/e2e/__tests__/os-shell.spec.ts is excluded by !web/e2e/**
  • web/e2e/__tests__/settings.spec.ts is excluded by !web/e2e/**
  • web/e2e/__tests__/storybook-bootstrap.spec.ts is excluded by !web/e2e/**
  • web/e2e/__tests__/tasks.spec.ts is excluded by !web/e2e/**
  • web/e2e/__tests__/worktrees.spec.ts is excluded by !web/e2e/**
  • web/e2e/fixtures/loop-run-reads.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/e2e/fixtures/retained-loop-task-seeder/main.go is excluded by !**/fixtures/**, !web/e2e/**
  • web/e2e/fixtures/retained-loop-task.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/e2e/fixtures/runtime-seed.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/e2e/fixtures/scenario-contracts.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/src/generated/compozy-openapi.d.ts is excluded by !**/generated/**, !**/generated/**, !**/*.d.ts
  • web/src/systems/loops/components/run-page/stories/goal-turn-timeline.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/loops/components/stories/loop-run-page.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/loops/components/stories/loop-run-registers.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/loops/components/stories/loop-runs.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/tasks/components/stories/tasks-list-surface.stories.tsx is excluded by !**/*.stories.tsx
📒 Files selected for processing (420)
  • internal/api/contract/loop_provenance.go
  • internal/api/contract/loop_reads.go
  • internal/api/contract/loop_runs.go
  • internal/api/contract/task_catalog.go
  • internal/api/contract/task_query_error.go
  • internal/api/contract/tasks.go
  • internal/api/core/conversions_tasks.go
  • internal/api/core/loop_interfaces.go
  • internal/api/core/loop_read_handlers.go
  • internal/api/core/loop_read_handlers_test.go
  • internal/api/core/loops.go
  • internal/api/core/loops_test.go
  • internal/api/core/task_catalog.go
  • internal/api/core/task_crud_handlers.go
  • internal/api/core/task_loop_filters.go
  • internal/api/core/task_payload_mapping.go
  • internal/api/core/task_query_error.go
  • internal/api/core/task_query_parsers.go
  • internal/api/core/tasks_surface_integration_test.go
  • internal/api/core/tasks_surface_internal_test.go
  • internal/api/core/tasks_surface_test.go
  • internal/api/httpapi/handlers_test.go
  • internal/api/httpapi/loops_routes.go
  • internal/api/httpapi/static.go
  • internal/api/httpapi/static_test.go
  • internal/api/httpapi/transport_parity_integration_test.go
  • internal/api/spec/loop_read_operations.go
  • internal/api/spec/loops.go
  • internal/api/spec/loops_test.go
  • internal/api/spec/parameters.go
  • internal/api/spec/registry_task_management.go
  • internal/api/spec/schema_enum_registry.go
  • internal/api/spec/spec_test.go
  • internal/api/testutil/task_loop_catalog.go
  • internal/api/udsapi/handlers_test.go
  • internal/api/udsapi/loops_routes.go
  • internal/api/udsapi/transport_parity_integration_test.go
  • internal/cli/client_loop_reads.go
  • internal/cli/client_loops.go
  • internal/cli/client_task_values.go
  • internal/cli/client_test.go
  • internal/cli/helpers_test.go
  • internal/cli/loop.go
  • internal/cli/loop_follow_timeline.go
  • internal/cli/loop_nodes.go
  • internal/cli/loop_read_commands.go
  • internal/cli/loop_read_commands_test.go
  • internal/cli/loop_read_table.go
  • internal/cli/loop_read_validation.go
  • internal/cli/loop_requests.go
  • internal/cli/loop_runs.go
  • internal/cli/loop_runs_output.go
  • internal/cli/loop_test.go
  • internal/cli/notifications.go
  • internal/cli/task_list.go
  • internal/cli/task_test.go
  • internal/codegen/sdkgo/generate_test.go
  • internal/codegen/sdkts/enum_registry.go
  • internal/codegen/sdkts/generate_test.go
  • internal/config/lifecycle/lifecycle_test.go
  • internal/config/loops.go
  • internal/config/loops_test.go
  • internal/config/merge_loops.go
  • internal/config/tool_surface_loops.go
  • internal/daemon/boot_components.go
  • internal/daemon/cmd_palette_integration_test.go
  • internal/daemon/daemon.go
  • internal/daemon/daemon_extension_commands_e2e_integration_test.go
  • internal/daemon/daemon_extension_view_program_e2e_integration_test.go
  • internal/daemon/daemon_implement_tasks_e2e_integration_test.go
  • internal/daemon/daemon_test.go
  • internal/daemon/hooks_bridge_loop_started.go
  • internal/daemon/hooks_bridge_loop_terminal.go
  • internal/daemon/hooks_bridge_watch_events.go
  • internal/daemon/host_api_options.go
  • internal/daemon/loop_api_reads.go
  • internal/daemon/loop_api_run_list.go
  • internal/daemon/loop_api_run_list_test.go
  • internal/daemon/loop_api_runs.go
  • internal/daemon/loop_api_service.go
  • internal/daemon/loop_effect_relay.go
  • internal/daemon/loop_goal_managed_runtime_integration_test.go
  • internal/daemon/loop_managed_binding_profile.go
  • internal/daemon/loop_managed_binding_projection.go
  • internal/daemon/loop_read_cli_e2e_integration_test.go
  • internal/daemon/loop_reconciler_boot.go
  • internal/daemon/loop_reconciler_runtime.go
  • internal/daemon/loop_run_events_e2e_integration_test.go
  • internal/daemon/loop_watch_events_payloads.go
  • internal/daemon/loop_worker_activation.go
  • internal/daemon/native_loop_tools.go
  • internal/daemon/native_loop_tools_support.go
  • internal/daemon/native_task_list.go
  • internal/daemon/native_tools_test.go
  • internal/daemon/runtime_workers.go
  • internal/daemon/task_run_activation_dispatch_test.go
  • internal/daemon/task_runtime_boot.go
  • internal/daemon/task_runtime_test.go
  • internal/extension/contract/sdk_enums.go
  • internal/extension/dev_integration_test.go
  • internal/extension/host_api.go
  • internal/extension/host_api_integration_test.go
  • internal/extension/host_api_options.go
  • internal/extension/host_api_task_payload.go
  • internal/extension/host_api_task_query.go
  • internal/extension/host_api_test.go
  • internal/loop/action_runagent.go
  • internal/loop/action_runloop.go
  • internal/loop/action_test.go
  • internal/loop/action_types.go
  • internal/loop/api_types.go
  • internal/loop/briefing.go
  • internal/loop/briefing_test.go
  • internal/loop/control_join_runtime.go
  • internal/loop/control_namespace_progress.go
  • internal/loop/control_plan.go
  • internal/loop/control_readiness.go
  • internal/loop/coordinator_action.go
  • internal/loop/coordinator_action_metadata.go
  • internal/loop/coordinator_autopause.go
  • internal/loop/coordinator_control_test.go
  • internal/loop/coordinator_lifecycle_test.go
  • internal/loop/coordinator_terminal_helpers.go
  • internal/loop/generation_snapshot.go
  • internal/loop/generation_snapshot_controls.go
  • internal/loop/generation_snapshot_test.go
  • internal/loop/node_requeue.go
  • internal/loop/reconciler.go
  • internal/loop/roster.go
  • internal/loop/roster_cursor.go
  • internal/loop/roster_helpers.go
  • internal/loop/roster_projection.go
  • internal/loop/roster_rollup.go
  • internal/loop/roster_routes.go
  • internal/loop/roster_test.go
  • internal/loop/run_read.go
  • internal/loop/run_read_briefing.go
  • internal/loop/run_read_service.go
  • internal/loop/run_summary.go
  • internal/loop/service.go
  • internal/loop/service_node_requeue.go
  • internal/loop/service_options.go
  • internal/loop/service_requests.go
  • internal/loop/settlement.go
  • internal/loop/timeline.go
  • internal/loop/timeline_view_test.go
  • internal/loop/timetravel_diff_outputs.go
  • internal/loop/worker_reactivation.go
  • internal/observe/tasks.go
  • internal/observe/tasks_filters.go
  • internal/observe/tasks_snapshot.go
  • internal/observe/tasks_test.go
  • internal/store/globaldb/global_db_loop_api.go
  • internal/store/globaldb/global_db_loop_api_test.go
  • internal/store/globaldb/global_db_loop_cancel.go
  • internal/store/globaldb/global_db_loop_coordinator_seed.go
  • internal/store/globaldb/global_db_loop_event_boundaries.go
  • internal/store/globaldb/global_db_loop_node_requeue.go
  • internal/store/globaldb/global_db_loop_node_requeue_runs.go
  • internal/store/globaldb/global_db_loop_read_artifacts.go
  • internal/store/globaldb/global_db_loop_read_integration_test.go
  • internal/store/globaldb/global_db_loop_read_roster.go
  • internal/store/globaldb/global_db_loop_run_reconciliation.go
  • internal/store/globaldb/global_db_loop_schema_integration_test.go
  • internal/store/globaldb/global_db_loop_summaries.go
  • internal/store/globaldb/global_db_loop_summary_projection.go
  • internal/store/globaldb/global_db_loop_task_lifecycle.go
  • internal/store/globaldb/global_db_loop_terminal_settlement.go
  • internal/store/globaldb/global_db_loop_test.go
  • internal/store/globaldb/global_db_loop_timetravel.go
  • internal/store/globaldb/global_db_loop_transition.go
  • internal/store/globaldb/global_db_task_catalog_scan.go
  • internal/store/globaldb/global_db_task_catalog_sql.go
  • internal/store/globaldb/global_db_task_claim_test.go
  • internal/store/globaldb/global_db_task_coordinator.go
  • internal/store/globaldb/global_db_task_coordinator_boundaries.go
  • internal/store/globaldb/global_db_task_coordinator_continue.go
  • internal/store/globaldb/global_db_task_coordinator_failure.go
  • internal/store/globaldb/global_db_task_coordinator_mutations.go
  • internal/store/globaldb/global_db_task_coordinator_runtime.go
  • internal/store/globaldb/global_db_task_coordinator_settlement.go
  • internal/store/globaldb/global_db_task_event_tx_test.go
  • internal/store/globaldb/global_db_task_inbox_sql.go
  • internal/store/globaldb/global_db_task_lease_recovery.go
  • internal/store/globaldb/global_db_task_status.go
  • internal/store/globaldb/global_db_task_test.go
  • internal/store/globaldb/loop_sqlc_mapping.go
  • internal/store/globaldb/queries/loop_core.sql
  • internal/store/globaldb/queries/loop_runtime.sql
  • internal/store/globaldb/queries/task_claim.sql
  • internal/store/globaldb/schema/definitions/50_loops.sql
  • internal/store/globaldb/schema/migrations/00081_schema.sql
  • internal/store/globaldb/sqlcgen/loop_core.sql.go
  • internal/store/globaldb/sqlcgen/loop_runtime.sql.go
  • internal/store/globaldb/sqlcgen/models.go
  • internal/store/globaldb/sqlcgen/task_claim.sql.go
  • internal/task/catalog.go
  • internal/task/inbox.go
  • internal/task/live_timeline.go
  • internal/task/live_types.go
  • internal/task/manager_coordinator_run_completion.go
  • internal/task/manager_test.go
  • internal/task/types_identity.go
  • internal/task/types_run_event.go
  • internal/task/validate_identity.go
  • internal/tools/builtin/builtin_test.go
  • internal/tools/builtin/loops.go
  • internal/tools/builtin/loops_output_schemas.go
  • internal/tools/builtin/task_list_schema.go
  • magefiles/e2e.go
  • magefiles/magefile_test.go
  • magefiles/web.go
  • sdk/go/contracts/types_013_gen.go
  • sdk/go/contracts/types_014_gen.go
  • sdk/go/contracts/types_015_gen.go
  • sdk/go/contracts/types_016_gen.go
  • sdk/go/contracts/types_017_gen.go
  • sdk/go/contracts/types_018_gen.go
  • sdk/go/contracts/types_019_gen.go
  • sdk/go/contracts/types_020_gen.go
  • sdk/go/contracts/types_021_gen.go
  • sdk/go/contracts/types_022_gen.go
  • sdk/go/contracts/types_023_gen.go
  • sdk/go/contracts/types_024_gen.go
  • sdk/go/contracts/types_025_gen.go
  • sdk/go/contracts/types_026_gen.go
  • sdk/go/contracts/types_027_gen.go
  • sdk/go/contracts/types_028_gen.go
  • sdk/go/contracts/types_029_gen.go
  • sdk/go/contracts/types_030_gen.go
  • web/src/storybook/__tests__/web-storybook-msw-contract.test.ts
  • web/src/storybook/__tests__/web-storybook-visual-contract.test.ts
  • web/src/systems/dashboard/components/home-dashboard.tsx
  • web/src/systems/loops/adapters/__tests__/loops-api.test.ts
  • web/src/systems/loops/adapters/goal-turns-api.ts
  • web/src/systems/loops/adapters/loop-roster-filters.ts
  • web/src/systems/loops/adapters/loop-run-reads-api.ts
  • web/src/systems/loops/adapters/loop-timeline-filters.ts
  • web/src/systems/loops/adapters/loops-api-errors.ts
  • web/src/systems/loops/adapters/loops-api.ts
  • web/src/systems/loops/adapters/loops-catalog-api.ts
  • web/src/systems/loops/adapters/loops-runs-api.ts
  • web/src/systems/loops/components/__tests__/goal-turn-timeline.test.tsx
  • web/src/systems/loops/components/__tests__/loop-run-page.test.tsx
  • web/src/systems/loops/components/__tests__/loop-runs-view.test.tsx
  • web/src/systems/loops/components/run-page/goal-turn-timeline.tsx
  • web/src/systems/loops/components/run-page/inspect/loop-generation-history.tsx
  • web/src/systems/loops/components/run-page/inspect/loop-node-panel.tsx
  • web/src/systems/loops/components/run-page/inspect/loop-node-roster.tsx
  • web/src/systems/loops/components/run-page/inspect/loop-run-dag.tsx
  • web/src/systems/loops/components/run-page/inspect/loop-run-events-lane.tsx
  • web/src/systems/loops/components/run-page/inspect/loop-run-inspect-register.tsx
  • web/src/systems/loops/components/run-page/inspect/loop-run-watch.tsx
  • web/src/systems/loops/components/run-page/loop-node-state-chip.tsx
  • web/src/systems/loops/components/run-page/loop-progress-segment-class.ts
  • web/src/systems/loops/components/run-page/loop-run-about-rail.tsx
  • web/src/systems/loops/components/run-page/loop-run-artifact-list.tsx
  • web/src/systems/loops/components/run-page/loop-run-briefing.tsx
  • web/src/systems/loops/components/run-page/loop-run-inspect-sections.tsx
  • web/src/systems/loops/components/run-page/loop-run-inspect-sheet.tsx
  • web/src/systems/loops/components/run-page/loop-run-lineage-section.tsx
  • web/src/systems/loops/components/run-page/loop-run-needs-you-card.tsx
  • web/src/systems/loops/components/run-page/loop-run-next-note.tsx
  • web/src/systems/loops/components/run-page/loop-run-now-card.tsx
  • web/src/systems/loops/components/run-page/loop-run-outcome-card.tsx
  • web/src/systems/loops/components/run-page/loop-run-page-body.tsx
  • web/src/systems/loops/components/run-page/loop-run-parked-panels.tsx
  • web/src/systems/loops/components/run-page/loop-run-progress-panel.tsx
  • web/src/systems/loops/components/run-page/loop-run-registers.tsx
  • web/src/systems/loops/components/run-page/loop-run-resolved-runtimes.tsx
  • web/src/systems/loops/components/run-page/loop-run-step-row.tsx
  • web/src/systems/loops/components/run-page/loop-run-steps-progress.tsx
  • web/src/systems/loops/components/run-page/loop-run-story-timeline.tsx
  • web/src/systems/loops/components/run-page/loop-run-story.tsx
  • web/src/systems/loops/components/run-page/loop-run-turns-disclosure.tsx
  • web/src/systems/loops/components/run-page/loop-run-waits-rail.tsx
  • web/src/systems/loops/components/run-page/loop-strategy-progress.tsx
  • web/src/systems/loops/components/run-page/requests/loop-request-card.tsx
  • web/src/systems/loops/components/runs/loop-node-inventory-view.tsx
  • web/src/systems/loops/components/runs/loop-run-row.tsx
  • web/src/systems/loops/components/runs/loop-runs-kpis.tsx
  • web/src/systems/loops/components/runs/loop-runs-table.tsx
  • web/src/systems/loops/components/runs/loop-runs-view.tsx
  • web/src/systems/loops/components/stories/loop-run-deep-graph-fixtures.ts
  • web/src/systems/loops/components/stories/loop-run-graph-eng-fixtures.ts
  • web/src/systems/loops/components/stories/loop-run-lifecycle-fixtures.ts
  • web/src/systems/loops/components/stories/loop-run-lifecycle-payloads.ts
  • web/src/systems/loops/components/stories/loop-run-metric-fixtures.ts
  • web/src/systems/loops/components/stories/loop-run-page-fixture-world.ts
  • web/src/systems/loops/components/stories/loop-run-page-fixtures.ts
  • web/src/systems/loops/components/stories/loop-run-read-builders.ts
  • web/src/systems/loops/components/stories/loop-run-register-fixtures.ts
  • web/src/systems/loops/components/stories/loop-run-scenario-props.ts
  • web/src/systems/loops/components/stories/loop-run-scenario-types.ts
  • web/src/systems/loops/components/stories/loop-run-timeline-from-frames.ts
  • web/src/systems/loops/components/stories/loop-run-vc-fixtures.ts
  • web/src/systems/loops/components/stories/loop-run-visual-contract.ts
  • web/src/systems/loops/components/stories/loop-runs-scale-fixtures.ts
  • web/src/systems/loops/hooks/__tests__/use-loop-request-attention.test.tsx
  • web/src/systems/loops/hooks/__tests__/use-loops.test.tsx
  • web/src/systems/loops/hooks/use-goal-turns.ts
  • web/src/systems/loops/hooks/use-loop-node-session-availability.ts
  • web/src/systems/loops/hooks/use-loop-request-attention.ts
  • web/src/systems/loops/hooks/use-loop-run-events-read.ts
  • web/src/systems/loops/hooks/use-loop-run-reads.ts
  • web/src/systems/loops/hooks/use-loop-stream.ts
  • web/src/systems/loops/index.ts
  • web/src/systems/loops/lib/__tests__/loop-generation-presentation.test.ts
  • web/src/systems/loops/lib/__tests__/loop-node-controls.test.ts
  • web/src/systems/loops/lib/__tests__/loop-node-panel-view.test.ts
  • web/src/systems/loops/lib/__tests__/loop-node-verb-target.test.ts
  • web/src/systems/loops/lib/__tests__/loop-request-model.test.ts
  • web/src/systems/loops/lib/__tests__/loop-run-artifacts.test.ts
  • web/src/systems/loops/lib/__tests__/loop-run-dag-view.test.ts
  • web/src/systems/loops/lib/__tests__/loop-run-live-seam.test.ts
  • web/src/systems/loops/lib/__tests__/loop-run-progress.test.ts
  • web/src/systems/loops/lib/__tests__/loop-run-roster-table.test.ts
  • web/src/systems/loops/lib/__tests__/loop-run-story.test.ts
  • web/src/systems/loops/lib/__tests__/loop-run-strategy.test.ts
  • web/src/systems/loops/lib/__tests__/loop-runs-view.test.ts
  • web/src/systems/loops/lib/__tests__/query-keys.test.ts
  • web/src/systems/loops/lib/__tests__/query-options.test.ts
  • web/src/systems/loops/lib/loop-formatters.ts
  • web/src/systems/loops/lib/loop-generation-presentation.ts
  • web/src/systems/loops/lib/loop-graph.ts
  • web/src/systems/loops/lib/loop-node-controls.ts
  • web/src/systems/loops/lib/loop-node-inventory.ts
  • web/src/systems/loops/lib/loop-node-labels.ts
  • web/src/systems/loops/lib/loop-node-lifecycle.ts
  • web/src/systems/loops/lib/loop-node-now-view.ts
  • web/src/systems/loops/lib/loop-node-panel-view.ts
  • web/src/systems/loops/lib/loop-node-verb-target.ts
  • web/src/systems/loops/lib/loop-request-model.ts
  • web/src/systems/loops/lib/loop-run-artifacts.ts
  • web/src/systems/loops/lib/loop-run-briefing-view.ts
  • web/src/systems/loops/lib/loop-run-dag-edges.ts
  • web/src/systems/loops/lib/loop-run-dag-view.ts
  • web/src/systems/loops/lib/loop-run-fanout-band.ts
  • web/src/systems/loops/lib/loop-run-live-seam.ts
  • web/src/systems/loops/lib/loop-run-next-note.ts
  • web/src/systems/loops/lib/loop-run-page-view.ts
  • web/src/systems/loops/lib/loop-run-progress.ts
  • web/src/systems/loops/lib/loop-run-registers-view.ts
  • web/src/systems/loops/lib/loop-run-roster-table.ts
  • web/src/systems/loops/lib/loop-run-state-copy.ts
  • web/src/systems/loops/lib/loop-run-story-beats.ts
  • web/src/systems/loops/lib/loop-run-story-lifecycle-rows.ts
  • web/src/systems/loops/lib/loop-run-story-request-rows.ts
  • web/src/systems/loops/lib/loop-run-story-rows.ts
  • web/src/systems/loops/lib/loop-run-story-types.ts
  • web/src/systems/loops/lib/loop-run-story.ts
  • web/src/systems/loops/lib/loop-run-strategy.ts
  • web/src/systems/loops/lib/loop-runs-view.ts
  • web/src/systems/loops/lib/loop-story-icons.ts
  • web/src/systems/loops/lib/loops-list-query.ts
  • web/src/systems/loops/lib/query-keys.ts
  • web/src/systems/loops/lib/query-options.ts
  • web/src/systems/loops/mocks/fixture-graph-eng-runs.ts
  • web/src/systems/loops/mocks/fixture-hero-path.ts
  • web/src/systems/loops/mocks/fixture-run-reads-contract.ts
  • web/src/systems/loops/mocks/fixture-run-reads-gated.ts
  • web/src/systems/loops/mocks/fixture-run-reads-nodes.ts
  • web/src/systems/loops/mocks/fixture-run-reads.ts
  • web/src/systems/loops/mocks/fixtures-config.ts
  • web/src/systems/loops/mocks/fixtures.ts
  • web/src/systems/loops/mocks/handlers.ts
  • web/src/systems/loops/mocks/index.ts
  • web/src/systems/loops/types.ts
  • web/src/systems/os/apps/loops/__tests__/loop-run-detail-location.test.tsx
  • web/src/systems/os/apps/loops/__tests__/loop-runs-location.test.tsx
  • web/src/systems/os/apps/loops/loop-run-detail-location.tsx
  • web/src/systems/os/apps/loops/loop-runs-location.tsx
  • web/src/systems/os/apps/loops/use-loop-run-detail.ts
  • web/src/systems/os/apps/loops/use-loop-run-page.ts
  • web/src/systems/os/apps/tasks/__tests__/tasks-catalog-location.test.tsx
  • web/src/systems/os/apps/tasks/tasks-catalog-location.tsx
  • web/src/systems/os/components/menubar/__tests__/menubar-command-item.test.tsx
  • web/src/systems/os/components/menubar/menubar-command-item.tsx
  • web/src/systems/os/components/os-palette-command-row.tsx
  • web/src/systems/os/hooks/__tests__/use-cmd-palette-catalog.test.tsx
  • web/src/systems/os/hooks/__tests__/use-cmd-palette-context.test.tsx
  • web/src/systems/os/hooks/__tests__/use-os-palette-root.test.tsx
  • web/src/systems/os/hooks/use-cmd-palette-catalog.ts
  • web/src/systems/os/hooks/use-cmd-palette-context.ts
  • web/src/systems/os/hooks/use-cmd-palette-dispatch.ts
  • web/src/systems/os/hooks/use-os-palette-root.ts
  • web/src/systems/os/hooks/use-os-palette-surface.ts
  • web/src/systems/settings/hooks/use-layouts-settings-data.ts
  • web/src/systems/tasks/components-api.ts
  • web/src/systems/tasks/components/__tests__/task-card.test.tsx
  • web/src/systems/tasks/components/__tests__/task-loop-provenance.test.tsx
  • web/src/systems/tasks/components/__tests__/tasks-list-row.test.tsx
  • web/src/systems/tasks/components/__tests__/tasks-list-surface.test.tsx
  • web/src/systems/tasks/components/public-api.ts
  • web/src/systems/tasks/components/stories/fixtures.ts
  • web/src/systems/tasks/components/task-card.tsx
  • web/src/systems/tasks/components/task-loop-provenance.tsx
  • web/src/systems/tasks/components/task-loop-row.tsx
  • web/src/systems/tasks/components/task-properties-rail.tsx
  • web/src/systems/tasks/components/task-rail-section.tsx
  • web/src/systems/tasks/components/task-subtask-list.tsx
  • web/src/systems/tasks/components/tasks-list-records-filter.tsx
  • web/src/systems/tasks/components/tasks-list-surface.tsx
  • web/src/systems/tasks/components/tasks-list-toolbar.tsx
  • web/src/systems/tasks/hooks/__tests__/use-tasks-page.test.tsx
  • web/src/systems/tasks/hooks/use-tasks-page.ts
  • web/src/systems/tasks/index.ts
  • web/src/systems/tasks/lib/__tests__/query-keys.test.ts
  • web/src/systems/tasks/lib/__tests__/task-hierarchy.test.ts
  • web/src/systems/tasks/lib/query-keys.ts
  • web/src/systems/tasks/lib/task-catalog-filter.ts
  • web/src/systems/tasks/lib/task-formatters.ts
  • web/src/systems/tasks/lib/task-hierarchy.ts
  • web/src/systems/tasks/lib/task-list-query.ts
  • web/src/systems/tasks/lib/task-loop-identity.ts
  • web/src/systems/tasks/mocks/__tests__/fixtures.test.ts
  • web/src/systems/tasks/mocks/fixtures.ts
  • web/src/systems/tasks/mocks/index.ts
  • web/src/systems/tasks/mocks/query-responses.ts
  • web/src/systems/tasks/types.ts

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


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.

@github-actions

github-actions Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

React Doctor found 4 issues in 3 files · 4 warnings · score 92 / 100 (Great) · vs main

4 warnings

src/systems/loops/lib/loop-run-dag-view.ts

  • ⚠️ L208 array.find() inside a loop js-index-maps
  • ⚠️ L209 array.find() inside a loop js-index-maps

src/systems/loops/lib/loop-run-progress.ts

  • ⚠️ L137 array.find() inside a loop js-index-maps

src/systems/loops/lib/loop-run-roster-table.ts

  • ⚠️ L260 array.find() inside a loop js-index-maps
⚠️ Warning: .github/workflows/react-doctor.yml is configured incorrectly. See below to fix.

React Doctor compares against main to report only the issues this pull request introduces. This run couldn't complete that comparison (usually a shallow CI checkout with no merge base), so it listed every issue in the changed files, including ones that already existed on main.

Add fetch-depth: 0 to the actions/checkout step in .github/workflows/react-doctor.yml so the checkout includes the history React Doctor needs:

 jobs:
   react-doctor:
     steps:
       - uses: actions/checkout@v5
+        with:
+          fetch-depth: 0

       - uses: millionco/react-doctor@v2

To silence this warning, set silence-missing-baseline-warning: true on the React Doctor action.

Reviewed by React Doctor for commit 5fcc3c9. See inline comments for fixes.


const dagNodes: LoopDagNode[] = [];
for (const nodeId of topoOrder(graph)) {
const authored = graph.nodes.find(entry => entry.id === nodeId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/js-index-maps (warning)

This gets slow as your list grows because array.find() runs inside a loop, so build a Map once before the loop for instant lookups

Fix → Build a Map once before the loop instead of calling array.find(...) inside it

Docs

const dagNodes: LoopDagNode[] = [];
for (const nodeId of topoOrder(graph)) {
const authored = graph.nodes.find(entry => entry.id === nodeId);
const rollup = roundRollups.find(entry => entry.node_id === nodeId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/js-index-maps (warning)

This gets slow as your list grows because array.find() runs inside a loop, so build a Map once before the loop for instant lookups

Fix → Build a Map once before the loop instead of calling array.find(...) inside it

Docs

): LoopStepRow[] {
const steps: LoopStepRow[] = [];
for (const nodeId of orderedNodeIds(graph, index)) {
const rollup = index.rollups.find(entry => entry.node_id === nodeId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/js-index-maps (warning)

This gets slow as your list grows because array.find() runs inside a loop, so build a Map once before the loop for instant lookups

Fix → Build a Map once before the loop instead of calling array.find(...) inside it

Docs

key: `${generation}:${nodeId}:container`,
nodeId,
isBranch: false,
kindLabel: loopDagKindLabel(graph?.nodes.find(entry => entry.id === nodeId)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/js-index-maps (warning)

This gets slow as your list grows because array.find() runs inside a loop, so build a Map once before the loop for instant lookups

Fix → Build a Map once before the loop instead of calling array.find(...) inside it

Docs

@pedronauck
pedronauck merged commit 1533e7b into main Aug 22, 2026
31 of 32 checks passed
@pedronauck
pedronauck deleted the loop-legibility branch August 22, 2026 20:53
AdemarTellecher pushed a commit to AdemarTellecher/compozy-os that referenced this pull request Aug 25, 2026
…op legibility)

Pull from origin/main bringing 54 commits including:
- feat: profiles — the who-is-working dimension (compozy#457)
- build: prepare release v0.3.0 (compozy#450)
- feat: complete production demo seed (compozy#453)
- fix: recover ACP sessions after provider disconnects (compozy#454)
- feat: make loop runs legible and task lists calm (compozy#452)
- and many more

Resolved Makefile .PHONY conflict by keeping both
cross-build-windows and build-windows targets alongside the
new test-e2e-desktop target.
This was referenced Aug 27, 2026
This was referenced Sep 15, 2026
This was referenced Sep 27, 2026

This branch was successfully deployed

1 active deployment
Preview — 5fcc3c9c Deployed Aug 22, 2026 by vercel[bot]
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