The Work merge is a routing and navigation change, and I could not click it. Open /admin/staff/work as owner, as staff and as an editor, in Command AND in SG. Confirm: one Work row in the rail; the editor sees only their own rows; the SG board still offers Board / Table / Jobs with every action; /admin/staff/action-queue?filter=overdue&client=X lands on /admin/staff/work with both parameters intact.
Editors in Command now work from the detail, not from cards. Claim / Start / Submit for review are on /admin/staff/work?deliverable=<id>. This is the one place the merge could have removed capability, and the one place I most want a real editor to try before this ships. The transitions are the same module the SG board uses (use-task-action.ts, lifted verbatim), and the server's compare-and-set is unchanged — but the *route* an editor takes to reach them is new.
tests/admin/action-queue-visibility.test.ts was rewritten. It is a security-adjacent test. It now asserts the data boundary (scopeActionQueueToEditor) instead of the rail, which I believe is the stronger guarantee — but the decision that "one Work route for every internal role, scoped server-side" is acceptable is Ivan's and Kris's, not mine. If they disagree, reverting is small: restore roles: STUDIO_ONLY on the internal Work row and give editors their own row again.
Home now makes a second read. /api/admin/action-queue in addition to /api/admin/launcher-summary. See §6.4 for the loader change that would remove it. Someone should decide whether the extra read is acceptable.
The waiting lane's membership — see §6.5. A product decision, one line to change.
Search copy. Work's global-search label changed from "Work / Jobs" to "Work". The words that were in the label moved into the detail line, which the search route also matches on, and the test now asserts that "jobs", "action queue", "my work" and "review" all still find the screen. Worth confirming in the real search box.
Everything visual. I have no browser here. The CSS changes — the wrapped phone rail, the stacked table, the sub-chip row, the action buttons, the dimmed zero tile — are reasoned, not seen. docs/design/command-redesign/ STATUS.md has been updated with the same account, for whoever picks this up. ---
I could not run the app. No Docker, no local Supabase, no .env and no .env.development.local in this container. Every claim below about *rendering* is reasoned from the code and the CSS, not observed in a browser. The visual approval this batch stops for is exactly what I could not do myself.
The test suite cannot go green here — see §7. This is the environmental failure CLAUDE.md already records, made total by the absence of any local database.
.cmd-scroll-x:has(...) — :has() is used once, to switch off horizontal scrolling when the table has become a stack. Supported in every current browser and in the Tauri/Electron shells; if it ever is not, the fallback is a scroller with nothing to scroll. Harmless, but worth knowing it is there.
Home now makes two reads (/api/admin/launcher-summary and /api/admin/action-queue). SG's Home already makes the second, so this is not new load for the product, but it is new load for Command's Home. If that matters, the honest fix is to serve the three launcher counts from the action-queue payload and drop the first read — a loader change, not a component change, and not something I should decide alone.
Open question for Ivan / Kris: the waiting lane groups new_intake (an unanswered enquiry), unassigned (nobody has picked it up) and waiting_review (nobody has looked). All three are "waiting on somebody at BULGAR", which is why they are together — but if the studio thinks of new enquiries as their own thing, that is a one-line change in WORK_LANE_CATEGORIES and the test that says every category has exactly one lane will keep it honest.
Still genuinely wrong, and NOT fixed here (carried forward from STATUS.md): Finance's lane filter does not filter; the tablet route reads the static gym fixture rather than the database; src/lib/email-outbox.ts is a localStorage mock. None is Batch 1. ---
Repo: /work/studio-website Branch: agent/bulgar-studio-studio-website/20260825T205700Z-0e86 Starting SHA: 366ffc48b3fa130273c844f0e4cdac85eee3f5d9 ("Merge pull request #7 from kristiyanpts/feat/bulgar-sg-design-system") Date: 25 August 2026 Scope: Batch 1, items 1–6. Batch 2 was not started. Not done, per instruction: no deploy, no push, no production data, no credentials touched, no test weakened or deleted, nothing outside studio-website.
There is no `/reference` directory and no `IMPLEMENTATION_HANDOFF.md` in this container. ls /reference, ls /work/../reference and a repo-wide search all came back empty. /work contains studio-website, superfit-gym-app, DATABASE_CHANGES_FOR_PARTNER.md and .claude. I proceeded from the repo and from the two documents that describe this redesign.
Read in full:
CLAUDE.md, AGENTS.mddocs/design/command-redesign/STATUS.md — what is bespoke, what inheritsdocs/design/command-redesign/BATCH_1_MAPPING.md — the REAL / DERIVED / FICTIONAL verdicts per conceptsrc/lib/design-system.ts usage, src/app/bulgar-command.css (1,347 lines)src/components/admin/admin-nav.ts, admin-shell.tsx, admin-command-shell.tsxsrc/domain/operations/action-queue.ts, home.ts, client-overview.ts, handoff.ts usagesrc/lib/server/action-queue.ts, src/app/api/admin/action-queue/route.tssrc/components/admin/admin-operations-workspace.tsxadmin-action-queue.tsxsrc/components/admin/{work,home, clients,calendar,team,inbox}/src/lib/domain/staff-calendar.ts, task-next-action.ts, business-lanes.tssrc/lib/hermes-screen.ts, src/lib/admin-destinations.ts, src/lib/admin-search.ts, src/app/api/admin/search/route.tssrc/app/admin/**/page.tsx route files (listing), and the affected tests in tests/admin/, tests/domain/, tests/hermes/Symptom (audit): /admin/staff/work renders a page titled "OPERATIONS"; /admin/operations also exists; the rail says "Work".
Root cause. Two components each believed they were Work, and the rail carried both:
| Route | Component | Title on screen | Rail row |
/admin/staff/action-queue | AdminActionQueue → CommandWork | "Work" | "Work" (owner/staff only) |
/admin/staff/work | AdminOperationsWorkspace | "Operations" | "My work" (editor only) |
/admin/operations | — | — | redirect to /admin/staff/work |
/admin/editor | — | — | redirect to /admin/staff/work |
internalNavForRole chose between the two rail rows *by role* — the studio-wide board for owner/staff, the cockpit for editors. So the role split was encoded as a URL split, and the second URL's screen called itself something else.
In Command mode this is exactly what the audit photographed: an owner clicking "Work" got the redesigned queue, but every link the domain produces — buildActionQueue defaults href to /admin/staff/work?deliverable=, and getAdminWorkHref produces /admin/staff/work?task= — lands on /admin/staff/work, which in Command rendered the SG board: three INTAKE LANE cards, nine count pills, a BOARD/TABLE/JOBS strip. ?task= was not even honoured by the Command branch (it only checked ?deliverable=), so roughly half the in-app links into a deliverable opened the board instead of the row they named.
Eight chips (Everything + seven categories). Accurate, and the wrong first question. The audit's "nine count pills of which seven show zero" is the SG board's intake lanes; the Command chip row has the same shape of problem at lower volume.
Root cause: a window mismatch, and only that. In src/components/admin/calendar/command-calendar.tsx the header rendered {events.length} events where events = getStaffCalendarEvents(state, dept) — every event in the workspace, for all time — while MonthView drew 42 cells starting at the Monday before the 1st of the anchor month, anchored on today.
The demo fixture's dated work is in May and June 2026 (src/lib/data.ts: scheduledShootDate: "2026-05-23", "2026-05-29", "2026-06-01"). Today is 25 August 2026. So the count was right, the grid was right, and they were describing different things.
I checked and ruled out the other candidates the brief named:
getStaffCalendarEvents reads useAdmin().state.studioToday() uses en-CA/Asia/Bangkok; byDay keys and grid cells are both YYYY-MM-DD and matched..cmd-month-grid, .cmd-day, .cmd-event all exist in bulgar-command.css:1347+; data-outside dims, it does not hide.SG does not have this bug — StaffGlobalCalendar filters getStaffCalendarEvents(...) by .startsWith(month) before counting (admin-separated-workspaces.tsx:5010). Command-only.
Two further defects found while fixing it:
step = view === "week" ? 7 : 28). Twenty- eight days is a month only in February: from the 1st of any 31-day month it lands on the 29th of the same month, so pressing "next month" on 1 January redrew January.addDays did new Date(iso + "T00:00:00") — parsed in the runtime's own zone — then setDate(+1). In a zone whose DST change is at midnight that can land back on the same date. Bangkok has no DST; a laptop in Santiago does..cmd-rail becomes flex-direction: row; overflow-x: auto, but no child was `flex: none` and .cmd-rail__tools additionally set min-width: 0. Flex items default to flex-shrink: 1, so instead of overflowing into the scroller the wordmark, the New button and the search input were all compressed past their content width and drew over each other. The overflow-x: auto never engaged because nothing ever overflowed..cmd-table td:first-child carries min-width: 11rem (9rem below 60rem) and the Work table has seven columns; at 358px of usable width the remaining six share ~215px, so prose cells wrap at every 3–4 characters — "Clea setu", "Revi inter work".buildHomeSummary and shipped inside /api/admin/action-queue as payload.home, and Command was not reading it. SG's Home V2 has rendered it since 19 August. Command was showing a strict subset of its own product.buildClientOverview and recentActivityForClient already exist, are pure, read the same store, and answer the two questions a person opens an account for — what is wrong with it, and what just happened.command-client.tsx linked each project to /admin/staff/work?project=<id>. filterActionQueue had no projectId filter, so the parameter was dropped and the whole board rendered. Found while working on item 5.
/admin/staff/workNew: src/components/admin/work/admin-work.tsx — the single entry point. It decides one thing: queue or one row's detail, in which presentation mode. It reads no data, computes no category, enforces no permission.
/admin/staff/work won the address, not /admin/staff/action-queue, because it is the one the domain already produced (buildActionQueue's default href, getAdminWorkHref) and the one /admin/operations and /admin/editor already redirect to. The route that lost is the one nothing pointed at.
| File | Change |
src/app/admin/staff/work/page.tsx | renders <AdminWork/> |
src/app/admin/staff/action-queue/page.tsx | server redirect to /admin/staff/work, query string carried across verbatim — every deep link into the queue is a filter, and dropping it would land somebody on the whole board and call that "the four overdue" |
admin-action-queue.tsx | AdminActionQueue → AdminActionQueueSg; the mode branch moved up to AdminWork |
admin-operations-workspace.tsx | AdminOperationsWorkspace → AdminOperationsBoard; titled Work, not Operations |
admin-nav.ts | staffNav and internalNav each carry ONE Work row at /admin/staff/work; internalNavForRole's one-of-two special case deleted |
admin-shell.tsx (OG rail) | same — it carried two items both labelled "Work" at two routes |
admin-command-palette.tsx | two entries ("Work", "Production queue") merged into one; the editor keywords folded in so "my work" and "jobs" still find it |
admin-destinations.ts | search label "Work / Jobs" → "Work"; the words that were in it moved to detail, which destinationGroups also matches on |
hermes-screen.ts | /admin/staff/action-queue moved from a described screen to the registered redirect list |
Home, launcher strip, Team, Finance, Partners, client command centre, domain/operations/home.ts | every internal /admin/staff/action-queue href updated, so the redirect serves bookmarks rather than the app's own links |
The role scoping did not go away. It moved to where it was always enforced. /api/admin/action-queue has served editors scopeActionQueueToEditor(...) since Phase 3 regardless of what the rail showed. canSeeActionQueue still decides which payload is sent and still decides the studio-wide framing on the screen. What stopped being a role decision is the URL.
Capability preserved, item by item (the list in the instruction):
| Capability | Where it is now |
| Enquiries + deliverables in one list | buildActionQueue — unchanged |
| Filter counts | lane chips + reason chips, counted across the whole queue |
| Owner filter | Command: Owner select (hidden for editors, whose payload carries no owner list) |
| Client filter | Command: Client select, same rule |
| State filters | five lanes over the seven real categories, all reachable |
| Deadlines | Due column, late in red, studio (Bangkok) day |
| Assignment | Owner column; "Nobody" in amber; Unassigned reason chip |
| Review state / delivery state / revision state | Status column (editorStatus ?? status), the Changes and Waiting lanes, and the five-state tracker on the detail |
| SG's Board / Table / Jobs views | kept, all three, and the queue added as their first view |
| Editor claim / start / submit | added to the Command work detail — see 2.5 |
New: src/domain/operations/work-lanes.ts (pure) — Now / Changes / Waiting / Upcoming / Done today as sets of the *existing* action categories.
overdue, due_todayneeds_changesnew_intake, unassigned, waiting_reviewupcomingcategoriesForWork returns early on completedAt. A done lane built from categories could only ever have read zero. It is fed from HomeSummary.momentum.completedToday — real completed_at rows the server already computes from the same deliverables in the same request — and it is called "Done today", because today is all that payload can prove.Guarantees, and they are tests: every category is in exactly one lane; a lane count is the length of the list it opens, distinct rows, never a sum (a row that is unassigned *and* awaiting review is one row in Waiting); the lane vocabulary may not contain the words capacity, utilisation, health, progress, score, or a percent sign.
The seven reasons survive as a second chip row inside an open lane, and ?filter=overdue still works — Home, the attention strip, Team and the client screen all deep-link an exact category, and a category always resolves to and lights the lane that holds it.
Command's Work no longer renders the intake-lane cards or the view strip. SG lost nothing: AdminOperationsBoard keeps Board / Table / Jobs and gains Queue as its first view (owner/staff only, canSeeActionQueue), and the intake lanes render only in the grouped views, where a lane is how the rows below are arranged rather than three cards of counts in front of the list they came from.
New: src/lib/domain/calendar-window.ts (pure, UTC) — one function says what a view is looking at; the header and the cells both read it, so they cannot disagree.
N events for the drawn window, and adds · M in all only when the two differ — so the common case is one number and the empty case explains itself.31 Jan + 1 month = 28 Feb, leap-aware).src/app/bulgar-command.css:
.cmd-wordmark, .cmd-rail__tools, .cmd-nav, .cmd-identity get flex: none. The strip now scrolls, which is what overflow-x: auto was there for; nothing is compressed past its content.<thead> is visually hidden but stays in the accessibility tree, and each cell prints its own column name from data-label. The title cell is the card headline — full width, no label, and released from the 11rem minimum that exists to stop numeric columns squeezing a name there are none of here. Opt-in because Command's money and configuration tables are meant to line up for comparison; they keep horizontal scroll until each is looked at properly.Owner Home (home/command-home.tsx) adds Today (shoots / due / deliveries) and Moving (finished today / ready to deliver / in production), read from payload.home on /api/admin/action-queue. A second read, in its own effect, deliberately silent on failure — Home never wears an error banner, so a failed read means those sections are absent, not that the page breaks. Lists cap at five rows and say + N more — no silent truncation. Empty groups render nothing. A stat tile reading 0 keeps its place and dims to 45% (data-quiet): a checked zero is worth knowing, and a tile that vanished would reflow the row under the cursor.
Client World (clients/command-client.tsx) leads the overview with Needs attention — buildClientOverview, which runs buildActionQueue narrowed to one client, so this screen cannot say "nothing outstanding" while Work lists three overdue rows for the same account. Below it: Recent activity (recentActivityForClient over activity_logs) and an At a glance count block (open deliverables, needing attention, due next week, nobody assigned, who is on the account, the active retainer month). Rows cap at six with "N more in Work".
projectId to ActionQueueFilters and to the Work hook; the Client World link that had been dropping it now works, and an active project filter renders as a removable chip so an inbound URL is visible rather than a silently smaller board. Leads are excluded from a project filter, which is correct — a lead is not part of a project until it becomes one.getAdminWorkHref writes ?task= and buildActionQueue writes ?deliverable=; both carry an order_deliverables.id. Command honoured only the second. Both now.src/components/admin/work/use-task-action.ts — the five workflow transitions (claim, start, submitForReview, requestRevision, approveTask), lifted verbatim out of admin-operations-workspace.tsx. The SG board and the Command detail now import one module. It decides nothing: getTaskNextAction says what a viewer may do, the server's compare-and-set decides whether it happens, and the two guards it carries are the same two the board had — they stop an obviously wrong request being sent, they are not the permission boundary.
The Command work detail was read-only, which was survivable only while /admin/staff/work still rendered the SG board underneath it. Merging Work removed that board from Command, so the transitions had to arrive on the detail or the merge would have quietly deleted an editor's ability to work. Manager Approve / Request changes is offered on the domain's own REVIEW_SUBMISSION code, not on a second reading of editorStatus.
Added — 4 files, 35 assertions:
| File | Pins |
tests/domain/operations/work-lanes.test.ts (15) | every category in exactly one lane; lane counts are distinct-row list lengths, not sums; done is fed from momentum and holds no queue rows; the lane vocabulary contains no capacity / utilisation / health / progress / score / % |
tests/domain/calendar/calendar-window.test.ts (20) | the header's window covers exactly the 42 cells the grid draws and no more; the reported bug reproduced as an assertion (an August anchor does not contain the fixture's May dates); nearest-date with forward tie-break; addDays(x, 28) from 1 January stays in January while addMonths does not; leap-aware clamping; identical answers under UTC / Bangkok / Santiago / Auckland |
Changed — 6 files. No assertion was weakened; where a premise changed, the replacement is stronger. Nothing was deleted to make something pass.
| File | Change |
tests/admin/action-queue-visibility.test.ts | The rail/palette assertions pinned "the rail does not offer editors the studio-wide queue". That premise is gone — there is one Work route for every internal role. Replaced with assertions on `scopeActionQueueToEditor`, which is the boundary that actually holds: an editor gets their own rows and nobody else's, no enquiry, no project-owned work, empty client and owner lists, and counts recomputed from the scoped list. Plus: the retired address is offered by no rail and no palette entry, and its page.tsx still exists so nothing 404s. 6 assertions → 13. |
tests/admin/admin-nav.test.ts | "narrows the staff rail for an editor by exactly the Action Queue" is no longer true and no longer desirable. Replaced with: the two roles get the same rail (because the data, not the rail, is scoped) and a new test that there is exactly one row labelled Work, at one href, in every rail, and that the retired href appears in none. |
tests/admin/attention-deep-links.test.ts | href literal only. What is asserted — the filter names a real category — is unchanged. |
tests/domain/operations/home.test.ts | href literal only. |
tests/admin/destinations.test.ts | Work's search label "Work / Jobs" → "Work". The old test asserted the spelling of one field; the new one asserts the thing that mattered — that "work", "jobs", "action queue", "my work" and "review" all still match, through adminSearchMatches over label *and* detail, which is what the search route does. Strictly more coverage. |
tests/domain/operations/action-queue.test.ts | Added the projectId filter: by project, combined with a category, and that a project filter excludes enquiries (with a positive control proving the enquiry is in the unfiltered queue). The shared fixture was left untouched — the lead case builds its own queue, because adding a lead to the shared one silently changed three unrelated assertions. |
buildActionQueue and getAdminWorkHref: the domain's canonical deliverable href is /admin/staff/work?deliverable= and getAdminWorkHref writes /admin/staff/work?task=, and /admin/operations and /admin/editor already redirect there. Retiring /admin/staff/work would have meant rewriting the domain's href construction for a cosmetic preference.?filter=overdue deep links from Home, the launcher's attention strip, command-team.tsx and admin-client-command-center.tsx. Replacing the vocabulary would have silently turned "4 overdue" into the whole board. The URL now accepts both, and a category lights its lane.categoriesForWork returns early on completedAt, so finished work is not in the queue at all. Rather than ship a permanently-zero chip (exactly the failure the audit reported: "nine count pills of which seven show zero") I traced where finished work *is* known — HomeSummary.momentum.completedToday, already in the same payload — and named the lane Done today, because today is the only span that data can prove.currentUser is null on the first render because the bootstrap has not arrived, so the default would have been computed from a role nobody has and never recomputed. An owner would have opened Work on the board for the rest of the session. Replaced with a nullable "chosen view" and a derived default.data-label, and stacking them would have produced unlabelled columns of numbers. The stack is opt-in (cmd-table--stack) and only Work uses it.bulgar-sg.css or OG rule was touched — every CSS change is inside bulgar-command.css, which is scoped to html.bulgar-command.StaffGlobalCalendar lives there and does not have the Command calendar's bug.WORKSPACE_ROLES, RBAC, the RLS policies, src/proxy.ts, any API route's permission check. No authorization was moved, widened or narrowed by this batch. The only change adjacent to it is that /admin/staff/work is now offered to owner, staff and editor in the rail — which it already was for editors, and which the API scopes.workInputsFromRows vs delivery-planner). CLAUDE.md records the 21 Aug decision to preserve the distinction and tests/domain/revision-vocabulary.test.ts enforces it. Left alone..env and no .env.development.local in this container. Every claim below about *rendering* is reasoned from the code and the CSS, not observed in a browser. The visual approval this batch stops for is exactly what I could not do myself.:has() is used once, to switch off horizontal scrolling when the table has become a stack. Supported in every current browser and in the Tauri/Electron shells; if it ever is not, the fallback is a scroller with nothing to scroll. Harmless, but worth knowing it is there./api/admin/launcher-summary and /api/admin/action-queue). SG's Home already makes the second, so this is not new load for the product, but it is new load for Command's Home. If that matters, the honest fix is to serve the three launcher counts from the action-queue payload and drop the first read — a loader change, not a component change, and not something I should decide alone.waiting lane groups new_intake (an unanswered enquiry), unassigned (nobody has picked it up) and waiting_review (nobody has looked). All three are "waiting on somebody at BULGAR", which is why they are together — but if the studio thinks of new enquiries as their own thing, that is a one-line change in WORK_LANE_CATEGORIES and the test that says every category has exactly one lane will keep it honest.src/lib/email-outbox.ts is a localStorage mock. None is Batch 1.Vocabulary is exact. Nothing below is reported as passing that I did not run.
$ npx tsc --noEmit (no output, exit 0)
Run repeatedly through the batch and clean at the end.
npx eslint on every touched path was clean throughout the batch (0 errors, 0 warnings). The whole-repo run is recorded below.
`npm test` as configured cannot run in this container at all.
$ npx vitest run
Error: Tests need a local DATABASE_URL; got "unset".
Test Files 117 failed (117)
Tests no testsRoot cause: tests/support/use-local-supabase.ts is a vitest setupFiles entry that throws unless DATABASE_URL points at a local Supabase. It exists to stop npm test mutating hosted production — the trap CLAUDE.md records — and it is correct. This container has no Docker (docker: command not found), no supabase stack, no .env and no .env.development.local, so there is nothing for it to point at. Every one of the 117 files fails at import, before a single assertion.
I did not weaken, skip or delete that guard. To get signal I set DATABASE_URL to the local address the guard asks for, for the duration of a run. Nothing is listening on it, so the suites that need a database fail on connect (TypeError: fetch failed in src/lib/supabase/server.ts) and the pure suites run normally.
Baseline, at the starting SHA, before any of my changes:
$ DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres npx vitest run
Test Files 24 failed | 93 passed (117)
Tests 33 failed | 1366 passed | 206 skipped (1605)All 33 baseline failures are fetch failed against the absent database. So the honest statement is:
The first attempt compiled and was then killed during TypeScript:
✓ Compiled successfully in 112s Running TypeScript ... Killed (exit 137)
exit 137 is SIGKILL by the OOM killer. cat /sys/fs/cgroup/memory.max → 2147483648 — this container is capped at 2 GiB, and memory.current sat at 2,050,256,896 while Next 16's Turbopack build and its TypeScript worker ran together. free -m reports the host's 8 GiB and is misleading here.
Note the same TypeScript configuration passes standalone (npx tsc --noEmit, §7.1) — it is the two running in one 2 GiB cgroup that does not fit.
Ranked by how much I would want a second pair of eyes on it.
/admin/staff/work as owner, as staff and as an editor, in Command AND in SG. Confirm: one Work row in the rail; the editor sees only their own rows; the SG board still offers Board / Table / Jobs with every action; /admin/staff/action-queue?filter=overdue&client=X lands on /admin/staff/work with both parameters intact./admin/staff/work?deliverable=<id>. This is the one place the merge could have removed capability, and the one place I most want a real editor to try before this ships. The transitions are the same module the SG board uses (use-task-action.ts, lifted verbatim), and the server's compare-and-set is unchanged — but the *route* an editor takes to reach them is new.scopeActionQueueToEditor) instead of the rail, which I believe is the stronger guarantee — but the decision that "one Work route for every internal role, scoped server-side" is acceptable is Ivan's and Kris's, not mine. If they disagree, reverting is small: restore roles: STUDIO_ONLY on the internal Work row and give editors their own row again./api/admin/action-queue in addition to /api/admin/launcher-summary. See §6.4 for the loader change that would remove it. Someone should decide whether the extra read is acceptable.docs/design/command-redesign/ STATUS.md has been updated with the same account, for whoever picks this up.Checked against the loader before use, as instructed:
| Board metric | Verdict | What is rendered instead |
| TEAM CAPACITY 78% | no data | nothing |
| TEAM LOAD 74/62/81% | no data | nothing |
| PROGRESS 74% | no data | the real five-state editor_status tracker (already there) |
| HEALTH: GOOD | no data | the account's real exception count and list |
| FILES 6 | no data | the three link fields that exist, empty ones absent |
| INBOX CHANNEL | no data | untouched — Inbox is not in this batch |
| DONE (a lane) | partly real, and proved | HomeSummary.momentum.completedToday = isCompletedToday(row.completedAt, todayIso) over live rows, computed by buildHomeSummary in src/domain/operations/home.ts and shipped in the action-queue payload. Real completed_at values, so the lane is real — but it can only speak for today, so it is named "Done today" and never "Done". |
| Calendar "N events" | real, and re-scoped | still a count of real events; it now counts the window on screen instead of the whole workspace, and shows the total separately when they differ. |
Every other number added in this batch is the length of a list of real rows, and tests/domain/operations/work-lanes.test.ts fails the build if the lane vocabulary ever mentions capacity, utilisation, health, progress, a score or a percent sign.
New (6)
src/components/admin/work/admin-work.tsx the one Work entry point src/components/admin/work/use-task-action.ts the five transitions, shared src/domain/operations/work-lanes.ts the five lanes (pure) src/lib/domain/calendar-window.ts what a calendar view draws (pure) tests/domain/operations/work-lanes.test.ts tests/domain/calendar/calendar-window.test.ts
Modified (30) — 24 source, 6 test. git diff --stat at the end of the batch: +1,604 / −403 across 30 tracked files, plus the 6 new ones.
Routes: src/app/admin/staff/work/page.tsx, src/app/admin/staff/action-queue/page.tsx. Styles: src/app/bulgar-command.css (Command-scoped only; SG and OG untouched). Components: admin-action-queue.tsx, admin-operations-workspace.tsx, admin-nav.ts, admin-shell.tsx, admin-command-palette.tsx, admin-workspace-launcher.tsx, admin-home-operations.tsx, admin-client-command-center.tsx, work/command-work.tsx, work/command-work-detail.tsx, work/use-action-queue.ts, home/command-home.tsx, clients/command-client.tsx, calendar/command-calendar.tsx, team/command-team.tsx, finance/command-finance.tsx, partners/command-partners.tsx. Domain / lib: domain/operations/action-queue.ts, domain/operations/home.ts, lib/admin-destinations.ts, lib/hermes-screen.ts. Docs: docs/design/command-redesign/STATUS.md. Tests: the six listed in §3.
Nothing was deleted. Two exported symbols were renamed (AdminActionQueue → AdminActionQueueSg, AdminOperationsWorkspace → AdminOperationsBoard); both had exactly one caller each, and both callers were updated.