J323: UI for the revoke access report (D356, #355) #406

Open
opened 2026-11-06 18:24:51 +00:00 by kayg · 9 comments
Owner

J323 (UI job) for D356: show the access-report after a host removal/revoke. Linked to #355 (backend: comment 82703).

Branch revoke-access-report-ui, stacked on revoke-access-report-d356b @ a0308b844.

Plan (est. 3-4 h):

  1. Specs first with the repo's test double: ready / pending / none / long / hostile text / stale session.
  2. A small reusable report panel in packages/ui (monospace lines via textContent only, scrolls inside the panel, plain explanation).
  3. Wire into host removal result (toast replaced by the panel when ready) and publish to the inbox (D234) so it stays until dismissed; quiet waiting state while pending; nothing for none.
  4. Stale session: same fresh-confirmation prompt that removal uses.
  5. Screenshots of a production build at 1440x900, 393x852, 375x667 in ink and paper; verify quick.
J323 (UI job) for D356: show the access-report after a host removal/revoke. Linked to #355 (backend: comment 82703). Branch `revoke-access-report-ui`, stacked on `revoke-access-report-d356b` @ a0308b844. Plan (est. 3-4 h): 1. Specs first with the repo's test double: ready / pending / none / long / hostile text / stale session. 2. A small reusable report panel in `packages/ui` (monospace lines via textContent only, scrolls inside the panel, plain explanation). 3. Wire into host removal result (toast replaced by the panel when `ready`) and publish to the inbox (D234) so it stays until dismissed; quiet waiting state while `pending`; nothing for `none`. 4. Stale session: same fresh-confirmation prompt that removal uses. 5. Screenshots of a production build at 1440x900, 393x852, 375x667 in ink and paper; `verify quick`.
Author
Owner

Started. Branch revoke-access-report-ui @ a0308b844 (stacked on d356b), worktree ~/Developer/Worktrees/infraternal-j323.

Findings so far:

  • No fresh-session gate exists on this base: the backend gives a bare 403 for every refusal, and removal today only toasts "Sign in again, then try again." I will treat a 403 on the report read as "stale" and offer a Confirm action that re-reads; when the gate lands with its own error shape I will map it. Question for the orchestrator: what status/body will the gate return?
  • Inbox items carry only a text body; I will add an optional content node so the file list can be a scrolling monospace panel (textContent only).
Started. Branch `revoke-access-report-ui` @ a0308b844 (stacked on d356b), worktree ~/Developer/Worktrees/infraternal-j323. Findings so far: - No fresh-session gate exists on this base: the backend gives a bare 403 for every refusal, and removal today only toasts "Sign in again, then try again." I will treat a 403 on the report read as "stale" and offer a Confirm action that re-reads; when the gate lands with its own error shape I will map it. Question for the orchestrator: what status/body will the gate return? - Inbox items carry only a text body; I will add an optional content node so the file list can be a scrolling monospace panel (textContent only).
Author
Owner

Progress: specs + implementation pushed as WIP (184f4c40c) on revoke-access-report-ui.

  • packages/ui/src/access-report.ts: strict reply reader + text-only scrolling view; inbox gains content node and open(id).
  • apps/tracer/src/canvas/access-report.on-demand.ts: pending -> quiet inbox line; ready -> toast + inbox panel (Done); none -> nothing; 403 -> "Sign in again to see what changed" toast with Check again.
  • Spec: apps/tracer/test/access-report.spec.ts (ready x3 sizes x2 themes, pending, none, long, hostile, stale).
  • Open: person names (the report only has person_id; no lookup exists in the client, so text says "Someone" until the owner API provides a name).
  • Waiting in build-slot queue on compute01 (position 11). Log: compute01 ~/j323-spec.log.
Progress: specs + implementation pushed as WIP (184f4c40c) on `revoke-access-report-ui`. - `packages/ui/src/access-report.ts`: strict reply reader + text-only scrolling view; inbox gains `content` node and `open(id)`. - `apps/tracer/src/canvas/access-report.on-demand.ts`: pending -> quiet inbox line; ready -> toast + inbox panel (Done); none -> nothing; 403 -> "Sign in again to see what changed" toast with Check again. - Spec: `apps/tracer/test/access-report.spec.ts` (ready x3 sizes x2 themes, pending, none, long, hostile, stale). - Open: person names (the report only has `person_id`; no lookup exists in the client, so text says "Someone" until the owner API provides a name). - Waiting in build-slot queue on compute01 (position 11). Log: compute01 ~/j323-spec.log.
Author
Owner

Status: first full run on compute01 (head 350a672d9): 9 passed, 2 failed, both spec mistakes (toast filter matched the old stale toast while it faded; long-report check looked for a path scrolled out of view). Fixed in 6f-next commit; resubmitted run was queued (position 17) and will be cut by the announced compute01 restart. I will resubmit once after "compute01 back" is posted on #402. Screenshots seen so far (ready, desktop/phone) look right; evidence dir on compute01: ~/.cache/codex-scratch/tracer-2462267/evidence/access-report.

Status: first full run on compute01 (head 350a672d9): 9 passed, 2 failed, both spec mistakes (toast filter matched the old stale toast while it faded; long-report check looked for a path scrolled out of view). Fixed in 6f-next commit; resubmitted run was queued (position 17) and will be cut by the announced compute01 restart. I will resubmit once after "compute01 back" is posted on #402. Screenshots seen so far (ready, desktop/phone) look right; evidence dir on compute01: ~/.cache/codex-scratch/tracer-2462267/evidence/access-report.
Author
Owner

J323 finished. Branch revoke-access-report-ui, head c1c336c3f5, stacked on d356b a0308b844. Not merged, not pushed beyond my branch. (The earlier queued run was cut by the compute01 restart; I resubmitted once.)

Two commits: feat(ui) (text-only report view, inbox content + open) and feat(tracer) (watcher wired into host removal, specs).

Behaviour: ready = toast with Review + inbox line (explanation, scrolling monospace file list, Done); pending = quiet "Waiting for " line, re-asked 3-20 s, dropped after 10 min; none = nothing; 403 = "Sign in again to see what changed on ." toast with Check again. Text only via textContent.

Spec apps/tracer/test/access-report.spec.ts on compute01 (log ~/j323-spec.log): 11 passed (2.3m), exit=0: ready x 3 sizes x ink/paper, pending, none, long (64 files, scrolls inside the panel), hostile text (no element created, no handler ran), stale.
Screenshots (1440x900, 393x852, 375x667, ink and paper) attached below; compute01 dir /home/claude/.cache/codex-scratch/tracer-1264320/evidence/access-report. I read them: the 375x667 first capture had Done pushed under the bottom bar; fixed with a shorter box on screens up to 700 px high, and the spec now checks Done is on screen and not covered.

verify quick (VERIFY_QUICK_BASE=origin/dev, compute01, log ~/j323-quick.log):

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| scope guard               | PASS         |
| migration names           | PASS         |
| Nix Cargo vendoring       | PASS         |
| fmt                       | PASS         |
| doc coverage ratchet      | PASS         |
| shape lints off (D351)    | PASS         |
| doc comments on diff      | PASS         |
| fixed test waits          | PASS         |
| clippy (workspace)        | PASS         |
| unit tests (changed)      | PASS         |
| unit tests (changed, bin-only) | PASS         |
| ui literals               | PASS         |
| tracer typecheck          | PASS         |
+---------------------------+--------------+
QUICK_EXIT=0

For kayg / orchestrator

  1. Names: the report has only person_id and the client has no lookup, so the text says "Someone". Needs a name in the API or a people lookup.
  2. Fresh-session gate: not on this base; any 403 is treated as stale. Map the gate's real error shape when it lands.
  3. Not re-fetched after a reload (the report lives on the server; I only start reading when a removal finishes).
  4. Not run: verify fast and the real test server (no deployed backend with this route).
**J323 finished.** Branch `revoke-access-report-ui`, head **c1c336c3f522d53c70bdba86235c3fee363f1ec1**, stacked on d356b a0308b844. Not merged, not pushed beyond my branch. (The earlier queued run was cut by the compute01 restart; I resubmitted once.) Two commits: `feat(ui)` (text-only report view, inbox `content` + `open`) and `feat(tracer)` (watcher wired into host removal, specs). **Behaviour:** ready = toast with Review + inbox line (explanation, scrolling monospace file list, Done); pending = quiet "Waiting for <host>" line, re-asked 3-20 s, dropped after 10 min; none = nothing; 403 = "Sign in again to see what changed on <host>." toast with Check again. Text only via textContent. **Spec** `apps/tracer/test/access-report.spec.ts` on compute01 (log ~/j323-spec.log): **11 passed (2.3m)**, exit=0: ready x 3 sizes x ink/paper, pending, none, long (64 files, scrolls inside the panel), hostile text (no element created, no handler ran), stale. Screenshots (1440x900, 393x852, 375x667, ink and paper) attached below; compute01 dir `/home/claude/.cache/codex-scratch/tracer-1264320/evidence/access-report`. I read them: the 375x667 first capture had Done pushed under the bottom bar; fixed with a shorter box on screens up to 700 px high, and the spec now checks Done is on screen and not covered. **verify quick** (VERIFY_QUICK_BASE=origin/dev, compute01, log ~/j323-quick.log): ``` +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | scope guard | PASS | | migration names | PASS | | Nix Cargo vendoring | PASS | | fmt | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | clippy (workspace) | PASS | | unit tests (changed) | PASS | | unit tests (changed, bin-only) | PASS | | ui literals | PASS | | tracer typecheck | PASS | +---------------------------+--------------+ QUICK_EXIT=0 ``` **For kayg / orchestrator** 1. Names: the report has only `person_id` and the client has no lookup, so the text says "Someone". Needs a name in the API or a people lookup. 2. Fresh-session gate: not on this base; any 403 is treated as stale. Map the gate's real error shape when it lands. 3. Not re-fetched after a reload (the report lives on the server; I only start reading when a removal finishes). 4. Not run: `verify fast` and the real test server (no deployed backend with this route).
Author
Owner

Holding at c1c336c3f per the orchestrator: D360 (dev 35dc4a738) changes the outcomes to changed / no changes found in N watched places since

Holding at c1c336c3f per the orchestrator: D360 (dev 35dc4a738) changes the outcomes to changed / no changes found in N watched places since <time> / not checked with a reason. Asked J216b on #355 for a display name and the per-person outcome fields. On the new API I will rebase and render all three; "No changes found" will be a visible quiet line, never the word "unchanged".
Author
Owner

J323: the three D360 outcomes are rendered. Branch revoke-access-report-ui, head c389e2e695, rebased onto revoke-access-report-d356b 7bb65d0ac (two commits on top). Not merged.

Rendering (API: state pending|reported|not_revoked, aggregate outcome, more, per-account facts)

  • changed: toast + inbox line (held until Done): per account its files, "new"/"removed" labels, notes, new lines in monospace; "N more changed files are not shown" / "Some lines were cut".
  • no changes found (API unchanged): a quiet, dismissible toast with the tick and no inbox line (it asks nothing): ": No changes found in the 214 places we watch (SSH keys, cron and at jobs, user services, shell startup files) since 14 Nov 2023, 22:13 UTC." Time is UTC with year and zone (from recorded_at); a missing count or time is left out, not guessed. The word "unchanged" never appears; the specs assert it on the page text.
  • not checked: toast + inbox line with each reason as a plain sentence (client map of the 10 codes; an unknown code gets a general sentence), the places not scanned, and the report's warning.
  • shared: "Shared account: changes may be someone else's." on the account. More than 16: "N more not shown." at the end.
  • Name: not in the API; still "Someone" (asked J216b on #355).
  • Everything is textContent; hostile text now also covers account names.

Specs (compute01, log ~/j323-spec.log): apps/tracer/test/access-report.spec.ts 23 passed (5.4m), exit=0 on a146964af (same code as the head; the later commits only squash and add one doc comment): changed, no changes found and not checked each at 3 sizes x ink/paper, pending, not revoked, 64-file long report, hostile, stale session.
Screenshots read (1440x900, 393x852, 375x667, ink and paper; compute01 /home/claude/.cache/codex-scratch/tracer-2366967/evidence/access-report); attached. Things I looked at: Done reachable at 375x667, the shared line and reasons wrap inside the box, the quiet toast stays inside the viewport.

verify quick (dev-02, VERIFY_QUICK_BASE=origin/dev, CARGO_INCREMENTAL=0, log ~/j323-quick.log; first run caught a missing doc comment on MONTHS, fixed):

+---------------------------+--------------+
| scope guard               | PASS         |
| migration names           | PASS         |
| Nix Cargo vendoring       | PASS         |
| fmt                       | PASS         |
| doc coverage ratchet      | PASS         |
| shape lints off (D351)    | PASS         |
| doc comments on diff      | PASS         |
| fixed test waits          | PASS         |
| clippy (workspace)        | PASS         |
| unit tests (changed)      | PASS         |
| unit tests (changed, bin-only) | PASS         |
| ui literals               | PASS         |
| tracer typecheck          | PASS         |
QUICK_EXIT=0

dev-02 target deleted. Not run: verify fast, real server (no deployed backend). Expect small renames from J343's re-review.

**J323: the three D360 outcomes are rendered.** Branch `revoke-access-report-ui`, head **c389e2e695a3baf8235c2890b9d1cedd0c4f78a3**, rebased onto `revoke-access-report-d356b` 7bb65d0ac (two commits on top). Not merged. **Rendering** (API: `state` pending|reported|not_revoked, aggregate `outcome`, `more`, per-account facts) - **changed:** toast + inbox line (held until Done): per account its files, "new"/"removed" labels, notes, new lines in monospace; "N more changed files are not shown" / "Some lines were cut". - **no changes found (API `unchanged`):** a quiet, dismissible toast with the tick and no inbox line (it asks nothing): "<host>: No changes found in the 214 places we watch (SSH keys, cron and at jobs, user services, shell startup files) since 14 Nov 2023, 22:13 UTC." Time is UTC with year and zone (from `recorded_at`); a missing count or time is left out, not guessed. The word "unchanged" never appears; the specs assert it on the page text. - **not checked:** toast + inbox line with each reason as a plain sentence (client map of the 10 codes; an unknown code gets a general sentence), the places not scanned, and the report's `warning`. - **shared:** "Shared account: changes may be someone else's." on the account. **More than 16:** "N more not shown." at the end. - **Name:** not in the API; still "Someone" (asked J216b on #355). - Everything is textContent; hostile text now also covers account names. **Specs** (compute01, log ~/j323-spec.log): `apps/tracer/test/access-report.spec.ts` **23 passed (5.4m), exit=0** on a146964af (same code as the head; the later commits only squash and add one doc comment): changed, no changes found and not checked each at 3 sizes x ink/paper, pending, not revoked, 64-file long report, hostile, stale session. Screenshots read (1440x900, 393x852, 375x667, ink and paper; compute01 `/home/claude/.cache/codex-scratch/tracer-2366967/evidence/access-report`); attached. Things I looked at: Done reachable at 375x667, the shared line and reasons wrap inside the box, the quiet toast stays inside the viewport. **verify quick** (dev-02, VERIFY_QUICK_BASE=origin/dev, CARGO_INCREMENTAL=0, log ~/j323-quick.log; first run caught a missing doc comment on MONTHS, fixed): ``` +---------------------------+--------------+ | scope guard | PASS | | migration names | PASS | | Nix Cargo vendoring | PASS | | fmt | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | clippy (workspace) | PASS | | unit tests (changed) | PASS | | unit tests (changed, bin-only) | PASS | | ui literals | PASS | | tracer typecheck | PASS | QUICK_EXIT=0 ``` dev-02 target deleted. Not run: verify fast, real server (no deployed backend). Expect small renames from J343's re-review.
Author
Owner

J323: person names in; rebased onto 71b07cc2d. Branch revoke-access-report-ui, head 992596d996 (two commits on top of 71b07cc2d). Not merged.

  • person_name is read per report (string or null) and used as text only, cut at 40 characters with an ellipsis; without a name the text says "Someone". It is no longer lower-cased in the toast ("offline-worker: Asha Rao changed 2 files while they had access."). Also read now: per-report omitted_accounts -> "N more accounts not shown". All other field names (state, outcome, more, accounts[].places/categories/recorded_at/shared/unscanned/omitted_files, reason, warning) match 71b07cc2d.
  • Rootless now reads: "infraternal runs without root on this host, so it cannot tell whether they changed the record it compares against. Look at their access files yourself." under "Not checked", as it always is for a rootless host.
  • Spec on compute01 (log ~/j323-spec.log): 25 passed (7.5m), exit=0 (23 earlier + a 200-character hostile name, and a report without a name). Same tree as the head (the later commits only squash). 25 screenshots attached (1440x900, 393x852, 375x667, ink and paper; compute01 /home/claude/.cache/codex-scratch/tracer-3410687/evidence/access-report); I read them, the name shows in the explanation and toast and nothing leaves the card.
  • verify quick (dev-02, VERIFY_QUICK_BASE=origin/dev, CARGO_INCREMENTAL=0, log ~/j323-quick.log): all 13 rows PASS, QUICK_EXIT=0. dev-02 target deleted.
  • Not run: verify fast, real server. Expect small renames from J357.
**J323: person names in; rebased onto 71b07cc2d.** Branch `revoke-access-report-ui`, head **992596d996af61060ccff46818ed9c8035cc2fe4** (two commits on top of 71b07cc2d). Not merged. - **`person_name`** is read per report (string or null) and used as text only, cut at 40 characters with an ellipsis; without a name the text says "Someone". It is no longer lower-cased in the toast ("offline-worker: Asha Rao changed 2 files while they had access."). Also read now: per-report `omitted_accounts` -> "N more accounts not shown". All other field names (`state`, `outcome`, `more`, `accounts[].places/categories/recorded_at/shared/unscanned/omitted_files`, `reason`, `warning`) match 71b07cc2d. - **Rootless** now reads: "infraternal runs without root on this host, so it cannot tell whether they changed the record it compares against. Look at their access files yourself." under "Not checked", as it always is for a rootless host. - **Spec** on compute01 (log ~/j323-spec.log): **25 passed (7.5m), exit=0** (23 earlier + a 200-character hostile name, and a report without a name). Same tree as the head (the later commits only squash). 25 screenshots attached (1440x900, 393x852, 375x667, ink and paper; compute01 `/home/claude/.cache/codex-scratch/tracer-3410687/evidence/access-report`); I read them, the name shows in the explanation and toast and nothing leaves the card. - **verify quick** (dev-02, VERIFY_QUICK_BASE=origin/dev, CARGO_INCREMENTAL=0, log ~/j323-quick.log): all 13 rows PASS, `QUICK_EXIT=0`. dev-02 target deleted. - Not run: verify fast, real server. Expect small renames from J357.
Author
Owner

J216b → J323: report API fields as of revoke-access-report-d356b @ 500feb193 (J345's round 5, D363/D364)

Nothing is renamed or removed. Compared with 71b07cc2d (the head your 992596d99 was built against), there are two changes:

  • New on each account: root_checks, a list of the root checks that actually ran, in plain words, e.g. ["sudo", "doas", "polkit", "admin groups"].

    • It can be empty or absent: a check that couldn't run makes the account not_checked: privileged, and an older agent sends nothing. Treat absent as [].
  • D364's clean sentence. For unchanged, the CLI now appends the root checks to D360's sentence:

    No changes found in the N places we watch (…) since

    It lists only the checks that ran on every account shown. With no checks, that second sentence is left out. The builder is no_changes_found in crates/infraternal/src/host_remove.rs.

  • D363, no new field. A process started by the system (a system unit running as the account) is never stopped. The account reads not_checked with reason: "programs_may_remain" and is listed in the existing processes array, the same shape as a shared account. The CLI wording is "Not checked: programs may still be running".

person_name (79a1b35fb) is unchanged.

**J216b → J323: report API fields as of `revoke-access-report-d356b` @ 500feb193** (J345's round 5, D363/D364) Nothing is renamed or removed. Compared with 71b07cc2d (the head your 992596d99 was built against), there are two changes: - **New on each account: `root_checks`**, a list of the root checks that actually ran, in plain words, e.g. `["sudo", "doas", "polkit", "admin groups"]`. - It can be empty or absent: a check that couldn't run makes the account `not_checked: privileged`, and an older agent sends nothing. Treat absent as `[]`. - **D364's clean sentence.** For `unchanged`, the CLI now appends the root checks to D360's sentence: > No changes found in the N places we watch (…) since <time>. They had no root access through sudo, doas, polkit or admin groups. It lists only the checks that ran on every account shown. With no checks, that second sentence is left out. The builder is `no_changes_found` in `crates/infraternal/src/host_remove.rs`. - **D363, no new field.** A process started by the system (a system unit running as the account) is never stopped. The account reads `not_checked` with `reason: "programs_may_remain"` and is listed in the existing `processes` array, the same shape as a shared account. The CLI wording is "Not checked: programs may still be running". `person_name` (79a1b35fb) is unchanged.
Author
Owner

Correction to 86336. With an empty or missing root_checks, the CLI does not print D360's sentence without its second part. It prints "Not checked: root access was not verified" instead: no "No changes found" without root checks.

  • With checks, the sentence is as quoted: "… since
  • The headline uses only the checks that ran on every account shown.
**Correction to 86336.** With an empty or missing `root_checks`, the CLI does **not** print D360's sentence without its second part. It prints **"Not checked: root access was not verified"** instead: no "No changes found" without root checks. - With checks, the sentence is as quoted: "… since <time>. They had no root access through sudo, doas, polkit or admin groups." - The headline uses only the checks that ran on every account shown.
Sign in to join this conversation.
No labels
launch-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/infraternal#406
No description provided.