webui: serve the Console first screen from the control board (#1507)
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# WEBUI-1507-R1
|
||||
|
||||
Author Dewey, independent reviewer Filbert, coordinator Darkwing.
|
||||
Workspace `/mnt/storage/src/mosaic-stack`, branch `refactor`, baseline
|
||||
355b4308c31c4bd500a844787eb1461f98dd2be3. Jason authorized issue-first,
|
||||
coordination, commit and push for plan piece 4. Issue #1507 is open; Gate E
|
||||
remains Jason-owned Monday 2026-09-14. No user acceptance is claimed.
|
||||
|
||||
## Candidate
|
||||
|
||||
All and only files under `packages/webui/`, pinned in
|
||||
`webui-1507/manifest.sha256`. Manifest SHA-256:
|
||||
`509f20e53292d5731c866d84c73cfaf539a89104b38b12a43260075ee7c3cb4d`.
|
||||
|
||||
No board files, seat files, comms, scanner, root package or fleet changes.
|
||||
Darkwing confirmed ownership and named you reviewer, transport rc 0 followed
|
||||
by his acknowledgement in this session. He released CURRENT and SESSIONS at
|
||||
fab40f25. Concurrent planning commits f80404f5/355b4308 preserved the intake
|
||||
status, with the shared-checkout hazard explicitly recorded in DEFERRED.
|
||||
Original uncommitted WUI drafts, skills and other owners' files are excluded.
|
||||
|
||||
Review for exact brief conformance, proxy boundary/refusals, draft and focus
|
||||
behavior, same-origin/loopback safety, rendering and maintainability. Reproduce
|
||||
checks and return APPROVED or concrete findings in
|
||||
`docs/plans/reviews/2026-09-13_webui-verdict.md`, then notify Dewey via
|
||||
agent-send. Do not edit source or publish. No real board scans or real-seat
|
||||
messages are needed for this review.
|
||||
|
||||
## Evidence
|
||||
|
||||
- `node --test packages/webui/tests/`: 6/6, including two Chromium browser tests.
|
||||
- Clean baseline-plus-candidate copy at `/tmp/webui-1507-clean-8KGiHN`:
|
||||
`node --test packages/webui/tests/ packages/control-board/tests/
|
||||
packages/seat/tests/ packages/ledger/tests/ packages/mosaic/tests/`: 208/208.
|
||||
Output: `webui-1507/clean-tests.txt`.
|
||||
- Browser checks use the real board scanner/server with temporary fixtures,
|
||||
including registered, working, waiting, error and offline rows. Only the
|
||||
board transport is stubbed. Exact counts and Seen/Reply round trips pass.
|
||||
- Browser keyboard, draft/caret, failed receipt, concurrent send exclusion,
|
||||
empty/loading/malformed/unreachable, stale registration, hostile/long text
|
||||
and denied localStorage cases pass. No real messages sent.
|
||||
- Rendered contrast across ten palettes and three modes: 330 samples pass,
|
||||
lowest 4.504658476260286:1. Five layout widths 320 through 2560px pass.
|
||||
- Screenshots in `webui-1507/`: Harbor light page at 320/1440 and Grove dark
|
||||
inspector at 320/1440. Dewey inspected all but the Harbor 320 screenshot
|
||||
before this request; that screenshot remains available to inspect.
|
||||
- CLI argument, upstream redirect, exact POST bytes/status, bad JSON/content
|
||||
type/size, hostile Host/Origin and asset allowlist cases pass.
|
||||
- No root CI workflow exists. No task/release/live fleet tests, deployment or
|
||||
owner acceptance is claimed. Direct refactor publication is authorized.
|
||||
|
||||
## Corrections during author verification
|
||||
|
||||
The first browser test used a wrongly quoted CSS selector; corrected the test.
|
||||
The Host test initially used fetch, which did not transmit the overridden Host;
|
||||
replaced that probe with node:http and verified rejection. The first clean-tree
|
||||
run exposed a loading-fixture race, ending the held response before the request
|
||||
arrived; an explicit request promise fixed it, then 208/208 passed. These were
|
||||
test defects, not waived failures.
|
||||
|
||||
## Scope choices and remaining gate
|
||||
|
||||
The left project filter uses actual board project names and counts, not mockup
|
||||
workspace registrations. Dense table fields and inspector detail come only from
|
||||
the board. Unsupported controller/generation/config/turns/activity and non-board
|
||||
controls are omitted and listed in DEFERRED. CSS/brand/fonts are copied unchanged;
|
||||
`live.css` supplies page adaptations. Appearance selectors remain on the one page.
|
||||
|
||||
Gate E requires Jason's Monday workday and end-of-day ruling. README gives the
|
||||
startup commands, test steps and rollback to the unchanged board. Keep #1507
|
||||
open until acceptance; no artificial issue closure from test results.
|
||||
@@ -0,0 +1,108 @@
|
||||
# WEBUI-1507-R1 review verdict
|
||||
|
||||
## APPROVED
|
||||
|
||||
Filbert, independent reviewer. Author Dewey. Reviewed 2026-09-13 against
|
||||
Piece 4 in `docs/plans/2026-09-12_control-board-mvp.md` and the request at
|
||||
`docs/plans/reviews/2026-09-13_webui-review-request.md`.
|
||||
|
||||
No blocking findings. Approval applies only to the exact package below. It
|
||||
passes independent implementation review, not publication, deployment or user
|
||||
acceptance. Gate E remains Jason's Monday 2026-09-14 ruling. Keep #1507 open
|
||||
pending that acceptance.
|
||||
|
||||
## Exact candidate
|
||||
|
||||
HEAD was the requested baseline
|
||||
`355b4308c31c4bd500a844787eb1461f98dd2be3` during provenance verification.
|
||||
The candidate was the untracked `packages/webui/` overlay, not a committed tree.
|
||||
|
||||
Manifest: `docs/plans/reviews/webui-1507/manifest.sha256`.
|
||||
Recomputed manifest SHA-256:
|
||||
`509f20e53292d5731c866d84c73cfaf539a89104b38b12a43260075ee7c3cb4d`.
|
||||
Its 21 entries cover all and only the 21 package files. All 21 content hashes
|
||||
passed verification before review, in the independent test copy, and after
|
||||
checks. This approval incorporates that exact manifest, not later package edits.
|
||||
|
||||
Behavior-bearing source hashes:
|
||||
|
||||
```text
|
||||
20f8091e5fb3fcaba583b601ce258999d44afbe569e93c979938ace759b4eef4 packages/webui/src/cli.mjs
|
||||
c0c1a360ce44a7439e67eea36b9237c59621aa3fd78571a823fd23dc15a950f6 packages/webui/src/serve.mjs
|
||||
7f30847ba25eb7e2b176df614d7bcae45ab7d86615c84ad44ffc29f0f261759c packages/webui/src/public/app.js
|
||||
db055d5b8a3710f4164193550ac25b7c5d223dd890ae3e9d73daaada38849557 packages/webui/src/public/index.html
|
||||
7add449911677c662a05ad5d9c2462f919e759574fe4b85bd13a705cc54f6856 packages/webui/src/public/live.css
|
||||
```
|
||||
|
||||
## Independently reproduced evidence
|
||||
|
||||
Created `/tmp/filbert-webui-1507-N4Iiyg` from a read-only `git archive` of
|
||||
baseline `packages`, `scripts`, and `tools`, then copied only the pinned WebUI
|
||||
package over it. No unrelated working-tree source entered this test copy.
|
||||
No dependency installation was needed. Node v26.8.1 and installed Chromium.
|
||||
|
||||
Commands run from that copy:
|
||||
|
||||
```sh
|
||||
sha256sum -c manifest.sha256
|
||||
WEBUI_EVIDENCE=/tmp/filbert-webui-1507-N4Iiyg/screenshots node --test packages/webui/tests/
|
||||
node --test packages/webui/tests/ packages/control-board/tests/ packages/seat/tests/ packages/ledger/tests/ packages/mosaic/tests/
|
||||
```
|
||||
|
||||
- WebUI: 6 passed, 0 failed, no skips, including both Chromium tests.
|
||||
- Combined suite: 208 passed, 0 failed, no skips.
|
||||
- Rendered contrast: 330 samples, no failures, minimum 4.504658476260286:1.
|
||||
- Layout checks passed at 320, 390, 768, 1440 and 2560px, with and without
|
||||
the inspector. Table-local horizontal scrolling is intentional.
|
||||
- Inspected all four independently generated screenshots: Harbor light page
|
||||
at 320/1440 and Grove dark inspector at 320/1440. Controls and text remain
|
||||
readable; the narrow inspector scrolls vertically to its reply controls.
|
||||
- `node --check` passed for both server modules, both public scripts and all
|
||||
five test modules.
|
||||
- `cmp` confirmed copied shared CSS, Console CSS, brand.js and all six font,
|
||||
source and license files are unchanged from Dewey's WUI assets.
|
||||
|
||||
Raw local results are `webui-tests.txt`, `combined-tests.txt` and `hashes.txt`
|
||||
in the independent copy. Screenshots are in its `screenshots/` directory.
|
||||
These temporary files supplement this durable result summary.
|
||||
|
||||
## Review conclusions
|
||||
|
||||
The page implements the approved Console layout using board data only. Project
|
||||
counts and filters do not hide another project's waiting requests. The dense
|
||||
table and inspector expose the specified fields; unsupported mockup controls
|
||||
are absent. Palette and appearance remain on this one page. Refresh uses the
|
||||
board page's ten-second cadence, with Pause and manual refresh, not a new scanner.
|
||||
|
||||
The server binds only to loopback, restricts the upstream to an HTTP loopback
|
||||
origin, normalizes localhost without DNS resolution, rejects hostile Host and
|
||||
Origin requests, and serves only allowlisted assets. POST routes require a JSON
|
||||
object and enforce the 4096-byte limit. The proxy preserves accepted POST bytes,
|
||||
upstream status and JSON text, refuses redirects, and does not retry actions.
|
||||
Seen and Reply reach only fixture transports during verification.
|
||||
|
||||
Rendering escapes dynamic text and attributes. Hostile identifiers do not enter
|
||||
CSS selectors. Drafts, receipts and caret positions survive refresh; stale
|
||||
registration removes reply access without discarding the draft. A per-row
|
||||
in-flight set prevents duplicate sends, and text changed during a send survives
|
||||
its earlier success. Keyboard row navigation, inspector focus and Escape return
|
||||
passed. Unreachable or malformed board data produces a warning without erasing
|
||||
the last valid snapshot. Denied localStorage falls back safely.
|
||||
|
||||
The implementation is small and separated into CLI, proxy and page behavior.
|
||||
Copied design assets remain separate from live-page adaptations. Source review
|
||||
and tests support the declared boundaries; no board, seat or comms change is
|
||||
part of this candidate.
|
||||
|
||||
## Limits and handoff
|
||||
|
||||
The checks used synthetic session and registration files and stubbed transport.
|
||||
No real board scan, real-seat message, private-session read, credential access,
|
||||
source edit or publication was performed. Git use was read-only provenance and
|
||||
archive extraction. Browser evidence is Chromium, not a screen-reader audit or
|
||||
Jason's workday acceptance. Contrast samples are not an exhaustive accessibility
|
||||
certification. The README documents in-memory draft loss on reload and uncertain
|
||||
delivery after a lost response.
|
||||
|
||||
Dewey should reverify the manifest before any separately authorized publication.
|
||||
CURRENT.md remains with its existing owner for the verdict/publication transition.
|
||||
@@ -0,0 +1,217 @@
|
||||
✔ loadConfig: missing file throws ConfigError (2.069893ms)
|
||||
✔ loadConfig: invalid JSON throws ConfigError (0.438417ms)
|
||||
✔ loadConfig: missing dataRoot throws ConfigError (0.327993ms)
|
||||
✔ loadConfig: relative dataRoot throws ConfigError (0.325869ms)
|
||||
✔ loadConfig: valid config returns dataRoot (1.031496ms)
|
||||
✔ findNewestSession: picks the newest by mtime among two files (0.731378ms)
|
||||
✔ findNewestSession: finds files in nested subdirectories (0.449258ms)
|
||||
✔ findNewestSession: returns null for a missing dir (0.190751ms)
|
||||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (1.108679ms)
|
||||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (2.038489ms)
|
||||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.855495ms)
|
||||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (2.03661ms)
|
||||
✔ deriveState: full state table (0.313836ms)
|
||||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.601595ms)
|
||||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.531506ms)
|
||||
✔ rule: a finished turn (text-only assistant message, stopReason stop) is waiting (0.405072ms)
|
||||
✔ task: the first user message of the session, from text blocks (0.491929ms)
|
||||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.381223ms)
|
||||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.472373ms)
|
||||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.567966ms)
|
||||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.643836ms)
|
||||
✔ scan: the written record carries task, workspace and activeProject (0.745408ms)
|
||||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.812363ms)
|
||||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.549512ms)
|
||||
✔ registration: a record whose pid is gone is stale; derived values win, sources say derived, registered stays with alive false; a pid the probe cannot decide is not stale; pidAlive itself (1.429642ms)
|
||||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.623553ms)
|
||||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.275783ms)
|
||||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (1.147462ms)
|
||||
✔ matchRegistration: matches by sessionsDir, and by realpath through a symlink; sessionsDir null never matches; same seat name with a different sessionsDir does not match (fleet vs repo darkwing) (1.76852ms)
|
||||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (1.131616ms)
|
||||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.313115ms)
|
||||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (1.127154ms)
|
||||
✔ scanAgent: ageSeconds is computed from the injected now (0.302815ms)
|
||||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.199626ms)
|
||||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.381178ms)
|
||||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.405132ms)
|
||||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (1.289574ms)
|
||||
✔ scan: relative boardDir throws ConfigError (0.150536ms)
|
||||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (89.928423ms)
|
||||
✔ CLI: missing config exits 2 with a refused: message (80.253972ms)
|
||||
✔ CLI: unknown command exits 2 (79.841572ms)
|
||||
✔ CLI: unknown --liveness value exits 2 (90.403332ms)
|
||||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.356649ms)
|
||||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.162436ms)
|
||||
✔ tmuxIsAlive: a pane running pi is alive (0.297307ms)
|
||||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.124004ms)
|
||||
✔ tmuxIsAlive: no such tmux session is not alive (1.002756ms)
|
||||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.141834ms)
|
||||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.188087ms)
|
||||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.141459ms)
|
||||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.129298ms)
|
||||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.172234ms)
|
||||
✔ loadSeen: missing file returns {} (1.594824ms)
|
||||
✔ loadSeen: invalid JSON throws ConfigError (2.10826ms)
|
||||
✔ loadSeen: a JSON array throws ConfigError (0.343731ms)
|
||||
✔ loadSeen: a non-string value throws ConfigError (4.345296ms)
|
||||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (3.563497ms)
|
||||
✔ markSeen: seen false deletes the key (1.941261ms)
|
||||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.477522ms)
|
||||
✔ markSeen: project containing '/' throws ConfigError (0.231097ms)
|
||||
✔ markSeen: non-boolean seen throws ConfigError (0.202643ms)
|
||||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (2.88064ms)
|
||||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.617591ms)
|
||||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (1.806863ms)
|
||||
✔ scanAgent: an error-state session with a matching mark is seen (2.149805ms)
|
||||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (1.045558ms)
|
||||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.490992ms)
|
||||
✔ isLoopbackHost: recognizes loopback hosts (2.380812ms)
|
||||
✔ isLoopbackHost: rejects non-loopback hosts (6.201413ms)
|
||||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (4.882151ms)
|
||||
✔ startServer: serves page, healthz, and a rescanning /api/board (48.899289ms)
|
||||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (11.47682ms)
|
||||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (4.702469ms)
|
||||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (78.932321ms)
|
||||
✔ CLI: serve rejects a non-numeric --port with exit 2 (82.056496ms)
|
||||
✔ CLI: scan still works after the async cli refactor (93.42089ms)
|
||||
✔ CLI: live serve prints its URL and answers /healthz (134.584373ms)
|
||||
✔ page.html: esc() escapes every HTML-significant character (1.293554ms)
|
||||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (19.569156ms)
|
||||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (5.81507ms)
|
||||
✔ POST /api/seen with invalid JSON returns 400 (8.413609ms)
|
||||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (6.922079ms)
|
||||
✔ POST /api/seen with a missing agent returns 400 (6.492325ms)
|
||||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (6.785092ms)
|
||||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (93.27849ms)
|
||||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.666541ms)
|
||||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (1.860189ms)
|
||||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.458153ms)
|
||||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.301181ms)
|
||||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.522645ms)
|
||||
✔ page.html: task and active project cells show their source via sourceTag(); the detail has a Registered row via registeredText(); SOURCE_LABEL maps registration to registered; every dynamic value in sourceTag/fromSource/registeredText is escaped (1.596776ms)
|
||||
✔ POST /api/reply: runs agent-send.sh with -s from the registration, -S <host>:control-board, -m text plus the fixed trailer, no -L on the default socket, MOSAIC_TMUX_SOCKET stripped; answers delivered with the exit code and both streams (66.023811ms)
|
||||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (47.085295ms)
|
||||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (44.906851ms)
|
||||
✔ POST /api/reply: refusals before the tool runs: empty or blank or long text 400, unknown row 404, no registration 409, stale registration 409, no tmux session 409, bad JSON 400; the tool is never called (30.336458ms)
|
||||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (6.232881ms)
|
||||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.196143ms)
|
||||
✔ page.html: the reply box appears only where canReply() holds (live registration with a tmux session), the detail has a Reply row, the submit posts JSON to /api/reply, receipts and drafts survive a refresh, and every receipt value is escaped (0.713094ms)
|
||||
✔ real helper GET HTTP 200 preserves exit 0 without credentials (16.87011ms)
|
||||
✔ real helper POST HTTP 201 preserves exit 0 without credentials (19.904021ms)
|
||||
✔ real helper GET HTTP 403 preserves exit 1 without credentials (14.960278ms)
|
||||
✔ fixture git subjects only, follow-ups and three session kinds (150.605163ms)
|
||||
✔ text and JSON carry same numbers, open and truncated title (323.809392ms)
|
||||
✔ missing credentials exit 2, no-issues never calls API and shows unknown (256.457428ms)
|
||||
✔ empty range gives no rows and zero totals (141.564883ms)
|
||||
✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (116.133329ms)
|
||||
✔ close-only issue included, even median, missing metadata stays unknown (88.374838ms)
|
||||
✔ unique commits but per-issue links count multiple tags once each (87.716176ms)
|
||||
✔ page cap refuses rather than silently undercounting (101.467302ms)
|
||||
✔ bad API payload not JSON refuses (91.969036ms)
|
||||
✔ bad API payload {} refuses (86.830577ms)
|
||||
✔ bad API payload [{"number":1}] refuses (77.291166ms)
|
||||
✔ partial or malformed session log refuses with location, not content (91.720579ms)
|
||||
✔ no sessions is an empty table; symlink source refuses (138.05013ms)
|
||||
✔ reads only refactor even when another branch is checked out (85.265578ms)
|
||||
✔ invalid dates, reverse dates and duplicate options refuse (46.116248ms)
|
||||
✔ preamble parsing and issue number boundaries (0.357602ms)
|
||||
✔ no closed issues with human messages means undefined ratio, not invented zero (0.191973ms)
|
||||
✔ pure resolution selects current default or explicit enrolled account (3.452427ms)
|
||||
✔ scope is explicit, bounded and never inferred (3.224534ms)
|
||||
✔ fork pin is preserved against default change, override, missing account and revocation (2.07011ms)
|
||||
✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (1.785801ms)
|
||||
✔ only explicit synthetic credential forms and internal fixture stores admitted (12.853623ms)
|
||||
✔ two concurrent workspaces of the same agent publish distinct complete private generations (89.218237ms)
|
||||
✔ same execution ID is exclusively claimed and cannot overwrite a published generation (51.919745ms)
|
||||
✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (69.110981ms)
|
||||
✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (165.301085ms)
|
||||
✔ credential lock contention refuses without duplicate side effects (34.503814ms)
|
||||
✔ symlinked pre-existing final target is refused and never followed (76.344676ms)
|
||||
✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.585699ms)
|
||||
✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (90.149481ms)
|
||||
✔ expired credentials refresh under transaction and subsequent generation reuses rotation (229.155792ms)
|
||||
✔ refresh failure retains prior generation and store state (80.103824ms)
|
||||
✔ refresh timeout retains prior generation and store state (166.055716ms)
|
||||
✔ refresh malformed retains prior generation and store state (68.191722ms)
|
||||
✔ concurrent refresh on same account refuses contention while unrelated account proceeds (218.21407ms)
|
||||
✔ invalid refresh options refuse before burning claim (39.334705ms)
|
||||
✔ fixed fake process rotates both OAuth fields without mutating caller input (51.399762ms)
|
||||
✔ concurrent isolated processes preserve separate provider credentials (55.271858ms)
|
||||
✔ fake failure is refused with fixed diagnostics (33.787807ms)
|
||||
✔ fake malformed is refused with fixed diagnostics (47.268272ms)
|
||||
✔ fake timeout is refused with fixed diagnostics (108.39385ms)
|
||||
✔ fake unchanged is refused with fixed diagnostics (64.498115ms)
|
||||
✔ caller executable/environment injection is rejected before spawning (0.483922ms)
|
||||
✔ valid fixture tree validates and lists without secrets (112.29331ms)
|
||||
✔ unknown-field refuses (0.679922ms)
|
||||
✔ invalid-id refuses uppercase and traversal shapes (0.462347ms)
|
||||
✔ plain-http baseUrl requires allowInsecureTransport (0.517778ms)
|
||||
✔ native provider rejects allowInsecureTransport (0.232811ms)
|
||||
✔ unsupported credential type and kind refuse (0.267028ms)
|
||||
✔ account provider-path mismatch refuses (1.053032ms)
|
||||
✔ profile account refs must be provider/account shaped (0.410258ms)
|
||||
✔ seat selection accepts fork pin field, validates account refs (0.385189ms)
|
||||
✔ harness manifest id must equal executable (gate 1) (0.471681ms)
|
||||
✔ CLI validate: duplicate provider id across files refuses (49.292022ms)
|
||||
✔ CLI validate: missing referenced provider/account refuse (56.231526ms)
|
||||
✔ CLI validate: broken JSON refuses without secret echo (44.514594ms)
|
||||
✔ CLI usage errors exit 2 (111.897184ms)
|
||||
✔ credential.json sibling presence does not break validation and is never read (168.781797ms)
|
||||
✔ D1 missing, empty and structurally empty roots refuse, no list projection (275.749732ms)
|
||||
✔ D1 required directory auth cannot be absent (124.619472ms)
|
||||
✔ D1 required directory auth/providers cannot be absent (136.518861ms)
|
||||
✔ D1 required directory auth/accounts cannot be absent (117.238735ms)
|
||||
✔ D1 required directory auth/settings cannot be absent (119.706456ms)
|
||||
✔ D1 required directory harnesses cannot be absent (97.054141ms)
|
||||
✔ D1 root file and unreadable metadata refuse (137.663685ms)
|
||||
✔ D2 no symlink traversal at auth/providers/openai-codex.json (57.104514ms)
|
||||
✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (58.8982ms)
|
||||
✔ D2 no symlink traversal at auth/providers (69.919373ms)
|
||||
✔ D2 no symlink traversal at auth (66.585499ms)
|
||||
✔ D2 root and ancestor symlinks and lexical traversal refuse (182.602198ms)
|
||||
✔ private filesystem modes enforced for root (56.988873ms)
|
||||
✔ private filesystem modes enforced for auth (54.402276ms)
|
||||
✔ private filesystem modes enforced for auth/providers/openai-codex.json (55.196762ms)
|
||||
✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (62.434238ms)
|
||||
✔ D3 numeric version 1 only across all record kinds (1.213381ms)
|
||||
✔ D4 nested unknown keys and missing per-kind required fields refuse (51.243059ms)
|
||||
✔ D5 unenrolled default refuses even when account exists (61.905203ms)
|
||||
✔ D6 provider/account credential type must match (61.029479ms)
|
||||
✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.495449ms)
|
||||
✔ D8 URLs reject embedded credentials and unsupported protocols without echo (164.103475ms)
|
||||
✔ D9 malformed JSON diagnostics contain no content excerpt (61.131821ms)
|
||||
✔ D10 missing metadata is missing-path, not invalid-json (61.521407ms)
|
||||
✔ D10 library returns no partial entries on any invalid record (70.423693ms)
|
||||
✔ null/scalar/array metadata refuses without stack or echo (216.403521ms)
|
||||
✔ credential sibling is never opened, even when an unreadable symlink (34.349719ms)
|
||||
✔ oversized metadata refuses before parsing (58.959915ms)
|
||||
✔ resolveSeat: by name under --repo resolves the repo layout (2.244578ms)
|
||||
✔ resolveSeat: by path resolves the fleet layout (0.655672ms)
|
||||
✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (1.497292ms)
|
||||
✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (1.278671ms)
|
||||
✔ makeRegistration produces a record that validates; each shape violation throws SeatError (1.478842ms)
|
||||
✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (1.781997ms)
|
||||
✔ updateTask: changes task and updatedAt only, and refuses appropriately (2.864265ms)
|
||||
✔ CLI launch: registers, execs the fake launch script, and passes args through (40.186244ms)
|
||||
✔ CLI launch: --harness lands in the record (62.351088ms)
|
||||
✔ CLI launch: the launch script's own exit code passes through (52.770779ms)
|
||||
✔ CLI launch: relaunching a seat rewrites the one registration record (103.782698ms)
|
||||
✔ CLI launch: omitting --task records an empty string, not null (68.271979ms)
|
||||
✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (201.947039ms)
|
||||
✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (249.955376ms)
|
||||
✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.580992ms)
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (1340.984357ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (1624.334009ms)
|
||||
✔ loopback host and board origin fail closed (7.093521ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (97.215606ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (36.079167ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (389.105161ms)
|
||||
ℹ tests 208
|
||||
ℹ suites 0
|
||||
ℹ pass 208
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2604.12737
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 167 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
@@ -0,0 +1,21 @@
|
||||
ee81d2b902a0b250f32d997d27085b9c76a7c98acdd0a0b55c65cf9342047b4e packages/webui/package.json
|
||||
6eca43194632017ffed73d62ba76c74435c4b661f4ec3fc76d8b0bae8bcf9089 packages/webui/README.md
|
||||
20f8091e5fb3fcaba583b601ce258999d44afbe569e93c979938ace759b4eef4 packages/webui/src/cli.mjs
|
||||
7f30847ba25eb7e2b176df614d7bcae45ab7d86615c84ad44ffc29f0f261759c packages/webui/src/public/app.js
|
||||
a30ddcd349703aff7464c34bef3fffdff405ee50c113440d7c8693c02d210972 packages/webui/src/public/assets/fonts/manrope-400.woff2
|
||||
a30ddcd349703aff7464c34bef3fffdff405ee50c113440d7c8693c02d210972 packages/webui/src/public/assets/fonts/manrope-500.woff2
|
||||
a30ddcd349703aff7464c34bef3fffdff405ee50c113440d7c8693c02d210972 packages/webui/src/public/assets/fonts/manrope-600.woff2
|
||||
a30ddcd349703aff7464c34bef3fffdff405ee50c113440d7c8693c02d210972 packages/webui/src/public/assets/fonts/manrope-700.woff2
|
||||
e01b637272e0cbdfb240184dd98ea5cc671556d9894dae2668d92ab2c906787c packages/webui/src/public/assets/fonts/manrope-OFL.txt
|
||||
0d9aaaaa963b675d94d8fbee0cf47a8ecf03ea412f8b14aba96416375e5a2bb5 packages/webui/src/public/assets/fonts/manrope-sources.txt
|
||||
d568df6dca14ae336db3b627639c8181c088649a21ffe992f9bfacc20b5b6238 packages/webui/src/public/brand.js
|
||||
1ce47447339a274dd51c0168838a5e58e0addaf2200e04625bc1da5bc9279097 packages/webui/src/public/console.css
|
||||
db055d5b8a3710f4164193550ac25b7c5d223dd890ae3e9d73daaada38849557 packages/webui/src/public/index.html
|
||||
7add449911677c662a05ad5d9c2462f919e759574fe4b85bd13a705cc54f6856 packages/webui/src/public/live.css
|
||||
b3e6e240fa93555e30321f897405839856b3438ef44b1cea73d2f5fbd899077e packages/webui/src/public/shared/app.css
|
||||
c0c1a360ce44a7439e67eea36b9237c59621aa3fd78571a823fd23dc15a950f6 packages/webui/src/serve.mjs
|
||||
2bf76719da97dfbad4edeeac98d608a4a8b09fccc2c1e42fba7cad61af205b0d packages/webui/tests/browser-edge.test.mjs
|
||||
3ff14de07e87cc624433db6425b9d05e40d98f61e40f2bbf1fbee7431f9d73c1 packages/webui/tests/browser.mjs
|
||||
f9cc0cf8603481a11f5eeb90ebc35fc5f1a7fff22a076e8a89eeec12cfd1700c packages/webui/tests/browser.test.mjs
|
||||
3887958f6c3343ac8d6db800725b75482cc66e559e6eb925b842eb9a5ae13bd2 packages/webui/tests/fixture.mjs
|
||||
15e77422890bc50cdb700484ca7e16ac32476aa8ec61d5575a6e0a920694bc4c packages/webui/tests/serve.test.mjs
|
||||
Reference in New Issue
Block a user