Skip to content

v1.2.0

Choose a tag to compare

@eengelking eengelking released this 19 Oct 23:44
· 147 commits to main since this release
f6b9a51

Changed

  • The Plan list puts finished steps at the bottom (#454). On the orchestrator page the Plan tab lists the step in progress first, then blocked steps, then the ones still to do in plan order, then the done steps in the order they finished. The open work stays on the first page of a long plan. A step the model reopens goes back among the open ones. The server numbers the finishes, so a reload keeps the order; steps already done when a plan first arrives come first among the done, in plan order. A step that moves keeps its row and your page, and a screen reader hears one polite line. The count, the bar and the Board keep plan order. See SPEC 8.1 and DECISIONS 38.

Fixed

  • My reporters counts a request's age by the server's clock (#467). "Asked 4 min ago" and "Proposed …" under Waiting for you used the browser's clock, while Your reporters used the server's, so a browser whose clock was off showed a request asked seconds ago as minutes old or in the future. They now use the server's time from the read, as Traffic does. The copied call(), DROP_WORDS and Blank on the messaging pages are one each, with no change to what the pages show or to the log's wording.
  • Admin and History draw the same top bar as every other page, and a page's own button is left off it (#468, #470). Admin had its own header with only "Back to fleet"; it now shows Fleet, Board, History, Traffic and the sign-in controls like the rest, and the locked, disabled and checking screens keep it. History was missing the Traffic button while orchestrator messaging is on. The Admin button no longer shows on Admin, the Traffic button on Traffic, or the My reporters link on My reporters, each page's pill already naming it. See SPEC 8.1.
    Standards: the top bar leaves off the button for the page you are on, now including Admin, Traffic and My reporters (approved by the maintainer on #468/#470); StreamTopBar is the shared bar for pages without a wall.
  • The Markdown "stays linear" test no longer times out in the Sonar scan job (#471). Its four heaviest cases built a node per marker at 50,000 repeats and took about 4.5 s each, so a slow coverage runner could pass the 20 s limit. N is now 15,000 and the backstop 60 s; the coverage run of the file drops from about 27 s to 9 s, and a quadratic renderer still fails every case it affects.
  • The Admin "changes the rows per page and returns to page one" test no longer times out on a slow runner (#477). It paged through 125 people with no limit of its own and took about 5.7 s on CI against the 5 s default. It now uses 60 people, enough for a second page and a smaller page size, and carries the same 60 s limit as the paging test above it; it still fails if Rows per page stops returning to page one. No other Admin test takes more than about 350 ms locally.
  • Plan finish order survives a report with no plan, and a replaced plan starts over (#465). A plan that went missing for a report and came back unchanged lost every finish number and fell back to plan order; the server now keeps the last plan across the gap. A new plan that reused the old step ids (TaskCreate numbers from 1) inherited the old numbers; plans now count as the same only with equal source, title and step texts, and any other starts over. Database migration 21 adds plan_kept_json. See SPEC 8.1 and DECISIONS 38.
  • SonarCloud reliability findings that failed the 1.2.0 release gate (#487). The standards scale reader and the doc-link checker's heading and link patterns no longer backtrack on long input, the Resume and Fork popup cells on the orchestrator page are a named group, and the Board's group list is sorted with a compare function. That last one changes the order of group names that differ in case: "alpha" now sorts before "Zeta". The group cells' markup gained role="group" and an aria-label, so the Standards components page was regenerated; nothing looks different.
  • The doc-link checker strips HTML tags from a heading until the text is stable (#489). This answers CodeQL's js/incomplete-multi-character-sanitization alert; the anchors it computes are unchanged. CodeQL's js/file-access-to-http alert on scripts/podium-peers-seed.mjs is a false positive (the only file data in a request is the repo's own package.json version, sent to the --url given), left for the maintainer to dismiss.
  • The diff redaction test builds its AWS temporary key id, Google key and Stripe webhook secret samples at runtime (#488), so GitHub secret scanning no longer flags test data. The redactor is checked on the same text as before.

Added

  • Title wall cards with the machine (#480). Admin > Settings > The wall has Title cards with the machine, off by default. On, each card on the wall is titled machine | session, such as Silverbolt | Foobar (the pipe set apart in the muted color), for everyone on this server and without a restart; off, titles are the session's name as before. A session with no known machine name keeps its session name, and one with no name shows its machine's. The rail, the Board and the orchestrator page are unchanged. The change is written to the Admin log. See SPEC 8.1.

  • Orchestrator messaging in Admin (#416, C5 of #347).

    • Settings. Admin > Settings > Feature flags has Orchestrator messaging (peer-messaging, off by default). While it is on, an Orchestrator messaging section sets the round trips before a person approves (10), messages per minute on a link (6), the longest message (20,000 characters) and how many days a conversation is kept (30). A value saves when its box loses focus or on Enter. One out of range is refused in place, and the error says nothing was saved and which value still applies.
    • A reporter's Messaging section. Admin > Reporters > a reporter shows the owner's reporters with the same Grid | Board as My reporters. A change is a proposal that shows as Proposed, waiting for the owner, until the owner accepts it. Only more than a pair has can be proposed: lowering a right or taking it away is the owner's choice. The server holds that line too: a proposal at or below the stored level is refused (409), and accepting a proposal, like allowing an access request, never lowers a right that rose since. Pause all links in the header applies at once, and Resume all links undoes it. The details add System and Messaging. A reporter on the admin secret, with no owner, with messaging off on the machine, or too old to message says so and offers no choices.
    • One read. The Admin's GET /api/admin/reporters/:key/peers now covers every pair of the owner's reporters and returns the owner's trust, so the Grid needs one read.
    • See SPEC 7.1, 7.7 and 8.1, and docs/SERVER.md.
    • Standards: a new shared component, the limit rows (components/PeerLimits.tsx: .srow rows with an in-place p.err refusal, a state line and a Use the Admin's limit button), for Admin > Settings and My reporters > Defaults. Approved with the #347 mockups (issuecomment-6257162320) and the #436 mockup, accepted by the maintainer on 2026-10-19 (#436 (comment)).
    • Standards: a new section, the Admin's Messaging section on a reporter (admin/ReporterMessaging.tsx), with Pause all links / Resume all links in the detail's header and a .warnbar for proposals and for the owner's Trust. Approved with the #347 mockup (issuecomment-6257162320); its cases with no design were accepted by the maintainer on 2026-10-19.
  • An owner's stricter limits on My reporters (#436, C7 of #347). My reporters > Defaults shows the three messaging limits: round trips before you approve, messages per minute on a link, and the longest message. Each box shows the limit that applies, and its most is the Admin's value. Use the Admin's limit, or typing the Admin's value, goes back to it, even when your own value is the same as the Admin's or above it. A value above the Admin's, below 1, not a whole number, or an empty box is refused in place, and nothing is saved. If the Admin later lowers theirs below yours, yours is kept and the row says the Admin's applies. See SPEC 8.1, docs/USER_GUIDE.md and mockup/podium-my-reporters.html.

  • Traffic (#417, C6 of #347). A console page at #/traffic, reached from Traffic in the top bar after History while orchestrator messaging is on.

    • The map. Inline SVG: a box per machine with its OS, each orchestrator with its status shape and state word, and a line per conversation. A line's state is its dash pattern plus an icon and a word on its label with the message count. An arrow shows a one-way link, and a thicker line means more messages. There is a legend.
    • The conversation. Selecting a line (click, or Enter on the focused line) opens the conversation beside the map. It has Pause link or Resume link, Remove link… with a confirmation, the round trips used against the limit, and the messages as an Activity feed with each one's delivery state. It also says how many days a conversation is kept, from a new retainDays field on GET /api/fleet/peers/traffic, which also gains serverTime (the Table counts "ago" by the server's clock) and a paused flag per conversation (a paused link with a message waiting for approval still reads as paused).
    • Table and Show. A Table view (where a narrow screen opens) and Show: Live sessions, Last 24 hours, Last 30 days.
    • Who reads what. Signed out, or not the owner, the map and counts show and the page never asks for the text.
    • scripts/podium-peers-seed.mjs --talk seeds conversations to look at. See SPEC 8.1, docs/USER_GUIDE.md and mockup/podium-traffic.html.
    • Standards: a new component, the Traffic map (components/TrafficMap.tsx; .pm-map, .pm-mapscroll, .pm-left, .pm-key-ink, .pm-key-warn, .pm-key-muted): machine boxes, nodes with StateIcon, lines as links with labels and arrows, and its legend. Approved by the maintainer with the #347 Traffic mockup (mockup/podium-traffic.html); the state word on a waiting or paused label and the other departures were accepted on 2026-10-19.
    • Standards: a new component, the Traffic conversation panel and page layout (Traffic.tsx; .pm-split, .pm-conv, .pm-tools, .pm-empty, .pm-err): a .feed of messages with delivery state, Pause/Remove link with .confirm, round trips, and the .offnote for anyone but the owner. Approved with the #347 Traffic mockup.
    • No right to send. A conversation whose two sides can no longer send, as after Remove link, reads "No right to send" with No access's icon in place of Active, in the conversation, the Table and the map's label. A paused link with a message waiting for approval shows Paused beside "A message waits for approval".
    • /podium deliver on a paused link says the message goes once the link is resumed; the session's answer route returns paused: true then.
    • Map edges. A line's label is kept inside the map, never cut off at its edge. A machine's box names its system from any live session on it, signed out or after the conversation's own sessions ended. A paused link with a message waiting reads "waiting, paused" on its label and in its accessible name.
    • Steady reads. The open conversation stays put when another gets a newer message, or when it drops out of the chosen range. A read that takes longer than the 10-second interval still lands, here and on a reporter's Messaging in Admin.
    • Standards: a new variant, a link's state as an icon and a word (.pm-state with .waiting and .paused). Approved with the #347 Traffic mockup.
    • Standards: the link-state tag (.pm-state) gains a "No right to send" state with No access's struck-circle icon, shown in place of Active when neither side can send; the map's line label carries the same icon and words, with no new stroke or legend row. Approved by the maintainer on 2026-10-19, after the end-to-end run.
  • The Board's KPI panel shows eight tiles (#450). Orchestrators live, running and waiting, Orchestrators with plans, Subagents live, running and waiting, and Total cards, which replaces Plan steps. They match the Fleet page's figures and stay whole fleet under Me, Group and Plans | All. Subagents waiting counts agents that are queued, a new Kpis.agentsWaiting the server derives. Four tiles across in two rows, two across at 640 px or less. See mockup/podium-board-kpi-tiles.html.

    • Standards: the Board component's sample changes (the kpis row has eight kpi tiles in place of four, and "Plan steps" is now "Total cards"); approved with the #450 mockup.
  • The Board's Me chip is now a My orchestrators | Everyone switch (#451). It sits after the Group dropdown in the same segmented control as Plans | All and Mixed | By orchestrator, named "Whose sessions". My orchestrators narrows the cards to the signed-in person's sessions and Everyone (the default) shows them all. As before it appears only while someone is signed in, never changes the tiles, and is remembered across a reload.

    • Standards: the Board component's sample changes (the kb-chip "Me" button is replaced by a kb-seg named "Whose sessions", and the kb-chip class is removed); approved with the #451 mockup.
  • A Plans | All switch on the Board (#426). At the upper left of the Board header, right after the title. All (the default) is the page as before. Plans shows only the columns and drops the strip of sessions with no plan, an empty one, or a finished one. The choice survives a reload. A session with no plan that waits on its person shows its Review card on All only (#443). The header sentence ("N orchestrators with open steps, M steps") is gone: two more tiles in the row under the header, Orchestrators with plans and Plan steps, state the same facts for the whole fleet, and neither the switch nor Me and Group changes them. See mockup/podium-kanban.html.

    • Standards: the Board component's sample changes (the kb-sum sentence is removed, a second kb-seg named "Sessions shown" and two more kpi tiles are added); approved with the #426 mockup.
  • podium-reporter can message your other orchestrators through Podium (#414, C3 of #347). A new peers option, "Message my other orchestrators" (off by default), gives the orchestrator three tools once Podium says the session takes part: podium_peers lists what it can see, podium_send sends, and podium_request_access asks its person for a right. See SPEC 5.3 and 5.9, docs/REPORTER.md.

    • Only the orchestrator. Any call from a subagent is refused, including the engine's own forks.
    • Sending. Anything that looks like a secret is replaced with [redacted] before the text leaves the machine, with the same redactor as a file's diff. The call is made by the reporter's timer with the report's credential and backoff, at once rather than at the next report, so a long report interval doesn't make a tool time out.
    • Receiving. A message starts a turn of its own once the session is idle, always under Podium's opening line saying it is another agent's text, never as the person. It never answers a question, approves anything, runs /clear or /compact, or changes a mode or permission. Tests fail if a message can be submitted without the opening line or can reach responding's answers.
    • Requests. An Admin's proposal, an orchestrator's request and a message waiting for approval show as a toast and a log line with a short id in the owner's sessions on that machine, answered with /podium allow|decline|deliver <id>. /podium allow and the asker's notice name the level Podium applied, which the answer route now returns as level. A waiting message's text and a request's reason are shown only in the toast, never to the model. /podium now runs at once during a turn.
    • The OS. The report carries the machine's OS and version (machine.os), read with sw_vers, /etc/os-release or ver.
  • My reporters (#415, C4 of #347). A console page at #/me/reporters where a signed-in person sees their own reporters, answers what waits for them (an Admin's proposal, an orchestrator's request for access, a message waiting for approval), sets their messaging defaults, and decides which reporters may see and message each other. The top bar reads Signed in as Ed, My reporters · N waiting (plain My reporters when nothing waits), Sign out. Your reporters shows each reporter's state and last report as Admin > Reporters does, from new state, lastSeenAt, compat, serverTime and serverStartedAt fields on the owner's read (so an outdated reporter is flagged even with no live session), and each request names both reporters by key (fromKey, toKey) so it lands on the right cell when two machines share a name. Who can message whom has a Grid view (rows send, columns receive, a select per cell) and a Board view (one reporter's peers in four columns, cards that drag or move with a Move to list), and the browser remembers which. Giving A the right to send to B asks at once whether B can answer. Each level is an icon and a word: No access, Can see, Can send, with approval, Can send. See SPEC 8.1, docs/USER_GUIDE.md and mockup/podium-my-reporters.html.

    • Standards: a new component, the access level (.pm-lv with lv-none, lv-see, lv-ask, lv-send): an icon and one label per level, never a hue. Approved by the maintainer with the #347 mockup (#347 (comment)).
    • Standards: a new component, the system tag (.pm-os): an icon for the system family and the name the reporter sent. Approved with the #347 mockup (issuecomment-6257162320).
    • Standards: two new tags, proposed (.pm-pend) and paused (.pm-state.paused). Approved with the #347 mockup (issuecomment-6257162320).
    • Standards: new components, the rights Grid (.pm-gridwrap, .pm-grid: rows send, columns receive, a hatched diagonal, raised cells for messaging off) and the rights Board (.pm-pick, .pm-board, .pm-col, .pm-card: four labelled columns, drag plus a Move to list), with the can-B-answer prompt (.pm-back), quoted text (.pm-quote) and notes (.pm-note, .pm-reqs, .pm-defs, .pm-name). Approved with the #347 mockup (issuecomment-6257162320).
    • Standards: the top bar has 16 px after "Signed in as " (.tb-who margin-right 8 px plus the 8 px gap), now that My reporters follows it. Approved on #347 (issuecomment-6257162320, decision 18).
  • The server can carry messages between one person's own orchestrators (#412, C2 of #347). This builds the server and protocol side. The mod's tools (#414), My reporters (#415), the Admin's pages (#416) and Traffic (#417) are above.

    • Switches. Off unless authentication is on, the Admin's new Orchestrator messaging flag is on (peer-messaging, off by default), the report says the reporter's peers option is on, and the owner gave that pair of reporters a right.
    • Rights. A right goes one way between two reporters: No access, Can see, Can send with approval, or Can send. A link can be paused. Rights only join two reporters with the same owner. The server refuses anything else, and any reporter on the admin secret, whatever the settings say.
    • Who can change a right. The owner sets rights and defaults: Trust my own reporters, what to do when an orchestrator asks for access, and how a new right to send starts. An Admin's change is a proposal the owner must accept. An Admin's Pause all links or removal applies at once.
    • Messages. A message is held until its receiver reports itself idle, and until the owner approves it where the link needs approval. It is dropped, and its sender told, if the receiver ends, the link is removed, or the flag goes off.
    • Limits. Round trips in a conversation before approval (10), messages per minute on a link (6) and the longest message (20,000 characters) are Admin settings. An owner can only make them stricter. Over a limit, a message is refused, never cut.
    • What is kept. The text is stored for 30 days after a link's last message and deleted with its sessions. Only its owner and an Admin can read it.
    • The record. Every message and right event goes to the Admin log and the server log, never the text.
    • New fields. The report has machine.os and session.peers. The reply has peers, peerDeliveries and peerRequests. Migration 20 adds the tables.
    • New routes. Mod routes under /api/fleet/sessions/:id/peers, the owner's under /api/fleet/respond/peers, an open GET /api/fleet/peers/traffic, and Admin routes for the limits, a reporter's links and the conversations.
    • Testing. scripts/podium-peers-demo.mjs plays the reporters against a running server.
    • See SPEC 6.1, 6.2, 7.1, 7.2 and 7.7, ARCHITECTURE "Orchestrator messaging", and .github/SECURITY.md.
  • The Board shows how many orchestrators and subagents are running (#409). Two tiles under the Board header, Orchestrators running and Subagents running, read the same figures the Fleet page already shows, so the pages agree. A session waiting on its person, idle, stale or ended is not counted, and a session that ends takes its subagents out of the count. The counts are always the whole fleet, whatever the Me filter, Group or layout shows, and they sit side by side on a narrow screen. Nothing new is sent by the mod or stored by the server. See mockup/podium-kanban.html.

  • The footer says which build is running (#400). A release shows Podium v1.1.0. A build of any other commit adds a Dev: a1b2c3d4 row above the version, the 8-character commit hash with -dirty after it when the tree had local edits, in the left list's footer and in Admin's. podiumctl build records the commit (PODIUM_BUILD in the image, server/build.json for a standalone build), and a release is a clean checkout of exactly a vMAJOR.MINOR.PATCH tag. The reporter mod does the same when it runs from a checkout: its status line reads podium v1.1.0 | dev: a1b2c3d4 ● 2 live in every connection state, /podium lists the reporter's and the server's builds, and the Reporter cell on a session's page reads 1.1.0 · Dev: a1b2c3d4. A build is sent as the optional reporter.build and serverBuild and stored as sessions.reporter_build (migration 19); an older mod or server is a release, so nothing changes for a mismatched pair. Standards: the left rail footer is now four rows (links, Dev, version, copyright) instead of two, with the copyright on its own row; accepted with the mockup mockup/podium-build-footer.html on #400.

  • Open a changed file's diff from the Files list (#319). With the reporter's fileDiff option on, the Admin's File diff switch on (Admin > Settings > Feature flags) and the session's owner signed in, each row in the Files tab is a button that opens the file's diff in the shared modal, side by side with the old file on the left and the new one on the right (one column in a window under 720px). Only the session's owner can open it: an Administrator who is not the owner cannot. The diff is read from the session's machine when the modal opens, cut at 2,000 lines or 200,000 bytes, and shown with a loading state and a plain message for a binary, too large, withheld or unavailable file. The mod hides anything that looks like a secret as [redacted] (provider tokens, JWTs, private keys, credentials in URLs, auth headers, secret-named keys, long random values) before the text leaves the machine, on removed lines too, and never reads .env, key or credentials files. Redaction is best effort. The server keeps the text only while the owner's browser waits: not in the database, the state, the stream or a log, and the Admin log records only that a path was opened. New: PUT /api/fleet/sessions/:id/diffs/:requestId (the mod), GET /api/fleet/respond/sessions/:id/files/diff (the owner's browser), diffs in the report reply, session.fileDiff in the report. Both switches are off by default. SPEC 5.7, 5.9, 6.1, 6.4, 7.1, 7.7, 8.1, DECISIONS 36, ARCHITECTURE "File diff".

    • Standards: a Files row that is a button (.files li.openable), a wide modal variant (.modal-diff) with a side-by-side diff that becomes one column below 720px, the bold critical [redacted] mark in a diff, and a loading state with three dots that fill in on the shared beat (a change from the plain "Loading…" word). Approved by the maintainer in the #319 mockup review.
  • Design standards for the UI, as pages in docs/standards/ (#354). Static HTML that opens from a checkout: every token by role, every theme in both modes, the sizes and durations in use, every component in its states rendered from the app with its own CSS, and the written rules for wall notices, paging, status, consent, copy, empty states, voice, accessibility and process. A page cannot drift from the app: npm run standards:parity fails on any difference, npm run standards:sync rewrites the pages after an approved change.

  • Two project skills and an audit agent for the design standards (#354). podium-standards-check checks UI work, or the whole app, against the standards and reports each deviation and what no standard covers. podium-standards-update changes the standards only after the maintainer's approval and does not finish until parity passes. podium-ui-auditor is the read-only agent for a checkup. CLAUDE.md names them and the standards, the release rules and the release check include parity, and a UI change that the maintainer approves adds a Standards: line under [Unreleased].

  • Checks that keep the UI's colors on the tokens (#354). npm test now fails on a hard-coded color outside tokens.css that is not listed, with a reason, in docs/standards/deferred.json; on a var(--x) that nothing defines; and on any theme and mode below the contrast targets. scripts/standards-inventory.ts lists the tokens, classes, components and color literals the checks read.

  • The left list pages when it would need a scrollbar (#242). With more sessions than fit the panel, the list shows one page of rows and the shared page footer in a compact form (Showing 1–8 of 24, Previous, 1 of 3, Next) at the bottom, directly above the credits footer; with few sessions nothing changes. A page is as many rows as the panel's height holds, so a taller window shows more. With people grouped, a band that continues onto the next page repeats its header, marked "continued". A selection on another page stays selected and the pager says which page holds it. A page change is announced to screen readers. The wall is unchanged.

  • An Administrator can delete people in Admin > People, one at a time or several at once (#243). Each row has a checkbox and a Delete button; ticking rows shows "N selected" with Delete N… and Clear, and both ways open a confirmation dialog that says the reporter is refused at once and past sessions keep their name. POST /api/admin/people/delete takes { ids } (1 to 500) and answers { ok, removed }, logging a person_deleted entry per person. It refuses, deleting nobody, if the list holds the person signed in or would leave Administrators with no active member; an unknown id is skipped. Sessions, spend and Admin log entries keep the name as text, and the name can be created again. SPEC 7.7, USER_GUIDE People page.

  • PODIUM_TRUST_PROXY lets the server trust a reverse proxy's client address (#305). Off by default, so a server with no proxy behaves as before. Set to a hop count, or addresses and ranges such as 10.0.0.5,172.16.0.0/12 (or loopback, linklocal, uniquelocal) with podiumctl config set, it goes to Express trust proxy: req.ip and req.secure then follow X-Forwarded-For and X-Forwarded-Proto from those hops only. Behind a TLS proxy the Admin log shows each person's address, the Admin, sign-in and admin-secret lockouts count each source apart, and the Admin and respond cookies are marked Secure. true is refused, and a value that does not parse stops the server at start. Trusting a hop that does not overwrite X-Forwarded-For lets any client choose its source; SERVER.md and SECURITY.md say so. SPEC 7.7, DECISIONS 35.

  • A Fork cell on the orchestrator page copies a command that forks the session (#352). cd <cwd> && claude --resume <id> --fork-session starts a new session with a copy of the conversation and leaves the original alone, so a stuck agent can be questioned without changing the session being investigated. It sits beside Resume, with the same "Copied" feedback. Hovering or focusing either cell opens a popup that says what the command does: Fork leaves this session unchanged, needs permissions approved again, works in the same directory so file edits are real, does not restore flags such as --mcp-config, and needs the transcript Claude Code keeps for 30 days; Resume continues this same session, to be run once it has ended. Escape closes a popup. The server builds forkCommand next to resumeCommand, with the same quoting for a path with spaces or shell characters. Nothing new is sent by the mod, stored by the server or logged, and Podium never runs the command. Not on History's read-only page. There is no worktree variant. (#352)

  • A kanban board of every session's plan steps (#346). A new page, #/board, reached from Board in the top bar (which also gains a Fleet button to go back to the wall), shows each plan step as a card in Pending, In progress, Review, Blocked or Done. Review holds an in-progress step while its session waits on its person. A click opens that orchestrator's page. Columns page at 25 cards, cards that change column slide and flash, Mixed or By orchestrator layouts and a Me filter (for a signed-in person) are remembered, and sessions with no plan or with everything done are listed under the board, with an (i) that explains how to get a plan. The board opens with the left and right panels collapsed and keeps those flags apart from the fleet page. Nothing new is sent by the mod or stored by the server.

  • A session you named in Claude Code takes that name in Podium (#322). The reporter reads the session's title from the SessionStart and UserPromptSubmit hooks, so a /rename shows from the next prompt, as the title on the card, the rail and the orchestrator page. /podium name still wins, and the Claude Code name ranks above the sessionName option and the worktree or folder name (SPEC 6.6). With no name set, nothing changes.

  • The card and the orchestrator page show the session id, and copy it on a click (#322). The card has the first eight characters at the right end of its path line; the orchestrator page already had them, and now they are a button. Either one copies the whole id.

Changed

  • A guide to orchestrator messaging, and a script to try it with real sessions (#347). docs/MESSAGING.md covers the whole setup from start to finish: how the server, podium-reporter, My reporters, Admin and Traffic fit, rights and approval, limits, what the session sees, what is kept, troubleshooting and how to test it. scripts/podium-peers-e2e.sh up builds the checkout (unless --no-build), starts a throwaway server from it, seeds it and starts Claude Code sessions in tmux that report to it; down stops them (--help, --dry-run). up refuses a work folder it did not make that is not empty, and down never removes /, the home folder, the checkout, a folder holding it, or a symbolic link. The other docs link to the guide, and GOTCHAS has an Orchestrator messaging section. Documentation and a test script only.

  • The peer-message framing and separation tests are shown to fail (#437, C8 of #347). The checks are now functions in mod/podium-reporter/hooks/peerChecks.ts, a test-only file like scripted.ts. The tests run them against the real code, and against a builder with no opening line and a receiver that hands a peer message to responding, both broken on purpose. The broken ones must fail, so no one has to remove Podium's opening line to prove the tests catch it. The failing output is on #437.

  • SPEC 5.3 records what the orchestrator messaging spike found (#411). Each mods-API point the #347 design rested on was checked in a real Claude Code 2.1.303 session, and SPEC 5.3 now marks it Verified or says what was found. A tool can't be kept from subagents, so it refuses any call that carries an agentId. A plugin's message waits for the turn to end, and background agents don't hold it back. The model doesn't treat the message as the person's words, and Podium's own opening line is required for it to refuse requests reliably. /podium must be registered with immediate: true to answer during a turn. The OS comes from sw_vers, /etc/os-release or cmd /c ver. Claude Code's own cross-session messages don't collide with Podium's. Nothing is built yet. ARCHITECTURE "Mod API findings" has the evidence.

  • The top bar hides and shows with an icon tab instead of a text button (#402, mockup/podium-top-bar-toggle.html). The "Hide top bar" button is gone from the bar's button row. A small tab with a chevron up sits at the middle of the bar's bottom edge, and once the bar is hidden the same tab hangs from the middle of the screen's top edge, chevron down, in place of the old "Show top bar" strip button. Neither shows text; each shows its name ("Hide top bar", "Show top bar") as a tooltip on hover and on keyboard focus, and keeps its aria-label, aria-expanded and aria-controls="podium-top". The tab is out of the bar's flex row, so it cannot cover or be moved by the logo, the pills or the buttons. The \ shortcut and the remembered state are unchanged. The hidden tab is always shown, where the old strip button was invisible until hover, and the hidden strip is 8 px tall instead of 4 px.

    • Standards: a new component, the top bar's icon tab (.tbar-tab), open and hidden, with a tooltip from data-tip; the hidden strip grows from 4 px to 8 px. Approved by the maintainer on the #402 mockup.
  • SonarCloud no longer counts the mod's hooks toward coverage (#400). The mod's tests import claude-code/testing, so they run only through npm run test:mod, which needs Claude Code installed and cannot run in CI. They could never feed the lcov report, so every line a mod change added read as uncovered and failed the New Code coverage gate. sonar.coverage.exclusions now lists mod/podium-reporter/hooks/**; duplication and the rules still apply to those files.

  • CI runs on the local self-hosted runners (#398). All five workflows (ci.yml, codeql.yml, sonar.yml, mod-version-check.yml, pr-target-check.yml) now use runs-on: [self-hosted, Linux, X64] instead of GitHub's ubuntu-latest, except that a PR from a fork runs on ubuntu-latest (a PR can edit the workflow it runs, so this saves the local runners and is not a security boundary; the approval setting for outside contributors is). Job names are unchanged, so branch protection still matches. When no runner is free a job queues; there is no fallback to a hosted runner.

  • Every color token now lives in one file, tokens.css (#354). Podium's own palette moved out of fleet.css and themes.css was folded in, so the app, the standards pages and new mockups read the same tokens. The page background reads --bg instead of three hex values. Nothing looks different.

  • The facts grid on the orchestrator page has ten cells, five to a row (#352). The session id moved out of the header line into a Session cell in the grid, where it still copies the whole id on a click, and a | now separates the working directory from the repository name on that line. Row one is Agents, Est. cost, Tokens, Running for, Agent time; row two is Blowouts, Reporter, Session, Resume, Fork. At 640 px and narrower it is two columns. Resume and Fork read "Command" with the copy icon instead of "Copy command". When the clipboard refuses, Resume no longer opens a text field; the cell stays as it was. The card's own session id is unchanged.

  • Promises in the wall-mode and copy code are awaited, not tested as booleans (#364). SonarCloud flagged a promise used as a condition (typescript:S6544) and a promise inside a try whose rejection the catch could not see (typescript:S4822). Entering wall mode now chains its refusal handler on the fullscreen request inside the try, and the copy controls (the session id, Resume and Fork) check that a clipboard exists before calling writeText, instead of testing the returned promise. A refused fullscreen request or clipboard changes nothing beyond what it did before; a browser with no clipboard or no fullscreen API now has a test.

  • The Containerfile and CI install dependencies with --ignore-scripts (#369). SonarCloud flagged npm ci in the Containerfile (docker:S6505, both stages) and in ci.yml (githubactions:S6505) because it runs every dependency's lifecycle scripts. No dependency needs one: the database is Node's built-in node:sqlite, and esbuild ships its binary as a platform package, so the image, npm test and npm run build behave the same without them. sonar.yml already did this. A future dependency that does need its script is rebuilt by name with npm rebuild <pkg>.

  • Number.NaN and Number.parseInt replace the globals (#366). SonarCloud flagged eight uses (typescript:S7773) in the activity parser, the model price editor and the theme contrast script. The values are identical, so nothing changes in behavior.

  • Sorted lists no longer depend on UTF-16 order, and one reduce() has a start value (#363). SonarCloud flagged two sort() calls with no compare function (typescript:S2871) and one reduce() with no initial value (typescript:S6959). The reporter's watched-loop ids and the Admin-added rows of the model price table now sort alphabetically with localeCompare, so Z no longer lands before a and an accented name no longer lands last. The tool-input trimmer starts its longest-text search from the first text. That reduce() was already guarded against an empty list, so nothing about it changes.

  • Regular expressions that could backtrack on long input are gone (#362). SonarCloud's typescript:S8786 flagged 18 regexes that take quadratic time on a long run of slashes, spaces, digits or [. The trailing-slash trims in the server, the mod and the simulator, the (Recommended) strippers, the mod's thousands separator, the markdown renderer's inline link and line patterns, and two patterns in scripts/theme-contrast.ts now use short linear code. What they match and what they return are unchanged, and each has a test with a 100,000-character input.

  • CodeQL scans the code on every push and PR (#345). .github/workflows/codeql.yml runs the security-extended queries over the server, UI and mod on pushes to development and main, on PRs into either, and weekly. Results appear under Security > Code scanning. The check is Analyze (javascript-typescript); making it required is a repository ruleset, set by the maintainer.

  • SonarCloud now gets test coverage (#371). npm run test:coverage runs the server and UI tests with coverage and writes lcov files. The Sonar workflow runs it before the scan, so the ≥ 80% coverage on new code condition judges real numbers instead of 0.0%. npm test is unchanged.

  • The "no open steps" strip is pinned to the bottom of the Board page (#359). It sits just above the legend with space around it, over the board, instead of floating under the tallest column. It stays put as cards come and go, the board still scrolls clear of it, a long list of sessions scrolls inside it past 200 px, and the (i) note opens above it and closes with Escape. Nothing is pinned when no session is without steps.

  • The docs are rewritten to be easier to scan (#351). The long files under docs/ now open with a contents list, keep paragraphs short, and puts reference material in tables and procedures in numbered lists. docs/GOTCHAS.md gives every entry the same three parts: what you see, why, and what to do. docs/USER_GUIDE.md is regrouped into get started, use the dashboard and run it, with the fixes for common problems near the top. docs/DECISIONS.md entries share one shape. Section numbers in docs/SPEC.md are unchanged, and the machine-specific path to the plugin API types is gone. The wording and structure changed, not what the docs say, apart from two corrections in docs/GOTCHAS.md (the mod version is bumped in the release-prep PR, and model prices are under Admin > Model prices). scripts/check-doc-links.mjs checks the relative links and anchors in the docs.

  • The README is now the introduction and the quick start, plus a table of the docs. The podiumctl, several-people and server-settings sections moved to the new docs/SERVER.md; the mod install, options, what leaves a session and settings hooks moved to the new docs/REPORTER.md; the checks list joined CONTRIBUTING.md. Links to those sections were updated. Documentation only.

  • The user guide and the mod doc give one command to update the mod. claude plugin marketplace update eengelking-marketplace && claude plugin update podium-reporter@eengelking-marketplace refreshes the listing and updates the mod in one go. Documentation only.

Fixed

  • My reporters has the orchestrator list, its footer and the Activity panel again (#446). The page drew only the top bar and the page. It now sits between the left rail and the Activity panel, as the Board does, and the rail's footer (Theme credits, License, the version, the copyright) is back in its bottom left corner. Both panels open collapsed to their edge tabs, and the page remembers them under podium.collapsed.reporters, apart from the Fleet page and the Board. They show the whole fleet, as the wall does, and picking an orchestrator or an activity event leaves for that orchestrator. Opening the page clears the selected orchestrator, as the Board does, and a failed Remove in the rail says so at the top of the page. [ and ] work there now; there is still no wall, so w does nothing, and no legend. See SPEC 8.1 and mockup/podium-my-reporters.html.
  • The plan tool names the plan again (#463). Its handler answered with an object, which Claude Code rejects (a tool's result has to be text, content blocks or nothing), so every call came back as an error and the console never got the title. It now answers in text, like the messaging tools.
  • History uses the same top bar as the other pages (#452). It drew its own header with one "Live fleet" link. It now shows the shared top bar: Fleet, Board and Admin buttons (no History button, since its pill names it), the live reporter count in the connection pill, and Sign in or "Signed in as". It has no Hide top bar control or wall button, as it has no wall.
  • The "N sessions have no open steps" strip no longer takes over a narrow Board (#413). When the Board is 640 px wide or less, the same width at which its columns turn into tabs, the strip is hidden and the columns have the full height. Widening the window brings it back with its list and note as they were. The sessions it names are still on the main list, which shows every session, so nothing is lost. Wide-screen behavior from #359 is unchanged.
  • The Markdown "stays linear" test no longer fails on a slow runner (#422). Each pathological case asserted that it rendered in under 3,000 ms, an absolute time that a busy runner or coverage instrumentation could cross without the renderer changing. It now renders each case at one size and at four times that size (best of three, after a warm-up) and asserts the second takes under 8 times the first, where linear is 4 and quadratic 16. The 20 s per-test timeout still catches a hang.
  • The left list shows its orchestrators again (#420). A list that was empty for a moment, as it is while the page connects, left the paging on page -1 for good, so no row showed and no pager appeared even after sessions arrived. The page now stays on the first when there are no pages. The paging arrived in #242.
  • The SonarCloud quality gate failures that came in with the design system (#396). String sorts in the standards generators now have a compare function, six regular expressions that could backtrack badly were rewritten to run in linear time (each has a test with a long hostile input), the all-screens test asserts what it rendered, http:// URL bases are https://, and samples.ts, a table of component entries, is left out of duplication only. The standards pages are unchanged.
  • A session the desktop app started as a spare reports the project it was moved to (#382). The app keeps pre-warmed spares in ~/.claude/spares/spare-XXXXXX and moves one to the project the person picks, without restarting it. The reporter read the session's place once, at session.start, so the card kept the spare's name, path and no git data. It now re-reads the place on each git poll, takes the cwd the SessionStart and UserPromptSubmit hooks carry when $.session still answers the spare folder, and, when $.session's root or repository does not hold the new folder, asks git there for the project root and repository name. The card shows the project's folder name, path and branch within one git poll. A session that never moves reports as before. Moving between two repositories keeps the stored git base (SPEC 6.7).
  • Admin > People "Machines seen" no longer grows without limit (#379). A machine whose report carries an id is listed once as its host name and the first four characters of its id, however many source addresses it reports from; a report with no id is listed by address. A person's list drops machines unseen for 30 days and keeps at most the 10 most recent. Migration 18 trims the rows already collected on upgrade. Search by machine still finds a person by a recent one, by name or id; a machine that sends an id is no longer found by its address. Reports that arrive through the settings hooks carry no machine, so they are still listed by address, inside the same cap. (#379)
  • The simulator no longer prints a server's reply or a person's name unfiltered (#367). scripts/podium-sim.ts turns newlines and other control characters into spaces before it logs a refused or rejected report, so a reply cannot forge a log line. The reporter's Math.random fallback is unchanged: it only runs where the sandbox has no crypto, and the value is a machine label, not a secret. Dev tool only; nothing changes for a running Podium.
  • The two sampling scripts use [[ instead of [ (#368). scripts/gpu-sample.sh and scripts/agent-tokens-sample.sh are bash scripts, so the six tests SonarCloud flagged (rule S7688) now use [[ ... ]], and the two tests joined by && in the token sampler became one. Behavior is unchanged; both scripts were run once afterward.
  • Four accessibility findings from SonarCloud are fixed (#365). The theme picker's Light, Dark and System buttons each handle the arrow keys themselves instead of the group around them, the sign-in page focuses the Name field from code instead of autoFocus, and the model price table's error is an alert inside its cell instead of on the cell. Keyboard behavior is unchanged.
  • The Board on Plans no longer lists a session that has no plan (#443). A session with no plan or an empty one that waited on its person still got a Review card on Plans. Now every column on Plans lists only sessions that have a plan; All still shows the card.