Observify

User Tests · mac-atlas
← all user tests
USER TEST PLAN

mac-atlas

Derived from: no git repo (Q-BACKUP: plain files, Wolf's call — see NOTES.md) — against ATLAS.html generated 2026-07-28T13:41:09Z (embedded totals 528 items / 53 live / 12 plates / 200 flagged), read and driven 2026-08-07.

0/69 checked · 0 pass · 0 fail

Flows

check each step and final check
F1

Open the atlas cold

Proves the core promise: a real double-click artifact, no server, no network, no broken render.

Persona / precondition: a fresh browser tab; you have not opened this file before (or don't care about prior state — the page keeps none).

0/6

Double-click ~/Projects/mac-atlas/ATLAS.html (or open it via File → Open in your browser).
Expect: the page renders immediately with no blank white screen and no browser "not
secure"/mixed-content warnings — it is a plain local file.

Open your browser's DevTools console (or the equivalent) and check for errors.
Expect: no red console errors on load. (I verified this myself via a local static-file
server, since some browsers refuse to run scripts on bare file:// — if yours does the same,
serve the folder locally, e.g. python3 -m http.server from ~/Projects/mac-atlas, and open
http://127.0.0.1:<port>/ATLAS.html instead. Either path renders identically; the page makes
zero network requests either way.)

Look at the browser tab title. Expect: Index Sheet — ATLAS.

Look at the masthead (top bar). Expect: the wordmark ATLAS, a search box with placeholder
Search everything… and a / key hint next to it, and a pill-shaped button on the left of the
search box showing the survey's age (checked in detail in F2).

Look at the footer. Expect: two key hints, / search and Esc back / clear.

Final check: open your network tab (or equivalent) and confirm zero outbound requests fired —
this page is advertised as making none, on any view.

F2

Index Sheet: the whole-machine numbers

Proves the headline figures are real and trace to the embedded data, not placeholder text.

Persona / precondition: F1 done; you're on the Index Sheet (#/ or no hash).

0/5

Read the four stat tiles under "Whole-machine survey". Expect: exactly 528 Items
surveyed
, 53 Live now, 12 Plates scanned, 200 Flagged rows. (I verified
these against the page's own embedded JSON — totals.items, .live, .surfaces, and the sum
of totals.by_flag — not just against what's printed on screen.)

Read the line under "Whole-machine survey". Expect: MacBook-Pro-6 · macOS 27.0 (arm64) ·
14 cores · surveyed July 28, 2026, 9:41 AM
.

Read the survey-age pill in the masthead. Expect: it reads Surveyed July 28, 2026, 9:41
AM · stale, N days ago
with a hollow-circle glyph (Unicode U+25CB), where N grows by one roughly once a day
from today onward. It will never say current or ageing again unless someone regenerates the
atlas — the rule (confirmed in the page's own script) is: under 6 hours old = current, under
48 hours = ageing, anything older = stale, permanently, until rebuilt.

Count the plate tiles below the stats. Expect: 12 tiles, numbered I through XII, titled
s1-launchd-user through s12-network-listeners. Each tile shows an item count and a live
count — spot-check V s5-homebrew: Expect: 109 items and 2 live.

Final check: click any plate tile (e.g. V s5-homebrew). Expect: it navigates to that
plate's own page (breadcrumb Index Sheet / s5-homebrew) — tiles are real links, not decoration.
Press Esc to return.

F3

Survey-age panel and the refresh instructions

Proves the page is honest about being a snapshot and hands you a real way to update it, without ever executing anything itself.

Persona / precondition: on the Index Sheet.

0/6

Click the survey-age pill in the masthead. Expect: a panel opens titled This atlas is a
point-in-time survey
, labeled Refreshing the survey above the heading, with a CLOSE button.

Read the first paragraph. Expect: it states plainly that a Mac does not hold still and that
the page can only tell you how old it is — not re-scan on its own.

Read the copy-ready commands. Expect: two code blocks, each with its own COPY button:
bash ~/Projects/mac-atlas/_build/refresh-atlas.sh and bash
~/Projects/mac-atlas/_build/refresh-atlas.sh --rescan
.

Click the first COPY button, then paste into any text field. Expect: the exact command
text lands in the field, and the button itself briefly shows a copied state before reverting to
COPY.

Press Esc. Expect: the panel closes and focus returns to the survey-age pill.

Final check: reopen the panel, then click somewhere else on the page outside it (not the
CLOSE button). Expect: it also closes — the panel dismisses on outside-click, not only on
Esc or CLOSE.

F4

Attack surface: what this machine exposes

Proves the security-relevant cross-cut works, and specifically that the one deliberate machine change this project ever made (removing an exposed database container) is correctly reflected — not a stale claim left over from before the change.

Persona / precondition: on the Index Sheet.

0/6

Click Attack surface in the left rail (or go to #/attack). Expect: breadcrumb Index
Sheet / Attack surface
, page heading Attack surface, section marker V6.

Read the intro line. Expect: it ends 6 listeners bind to a world-reachable address.

Read the section counts. Expect: Listening sockets (18), Privileged helpers (16),
System extensions (12), Every security-sensitive item (68). (Verified by re-running the
page's own listener/helper/extension/sensitive detection rules against the embedded JSON.)

In the listening-sockets table, look at the top rows. Expect: the 6 world-reachable rows
are sorted first (badge ▲ WORLD-REACHABLE), each one owned by apple — three pairs
(IPv4/IPv6) for ControlCe... on ports 7000 and 5000, and rapportd on a high port.
Loopback rows carry ● LOOPBACK instead.

Search the listening-sockets table (browser find, Cmd+F) for speedreader. Expect: ZERO
matches anywhere on this page. NOTES.md records that speedreader-db-1 — a Postgres container
published on 0.0.0.0:55432, once the only non-Apple world-reachable listener on the machine —
was deliberately removed on 2026-07-25 and the atlas rebuilt afterward. This step is the proof
that the removal actually made it into the shipped page, not just into the ledger prose.

Final check: click the legend's bind-address key at the right. Expect: it explains both
badges in plain language (▲ WORLD-REACHABLE = reachable from other machines on the network;
LOOPBACK
= reachable only from this machine) — the meaning never rests on color alone.

F5

Prune candidates: the practical payoff

Proves the actionable list is real, its kill commands are genuinely inert, and a specific low-risk candidate the ledger already names shows up correctly.

Persona / precondition: on the Index Sheet.

0/5

Click Prune candidates in the left rail (or go to #/prune). Expect: heading Prune
candidates
, intro line 64 items flagged orphaned, broken, duplicate, outdated, or
dangling-risk — the atlas's practical payoff.
(64 is exact — verified by re-summing the
embedded flag counts myself, not just reading the on-page number.)

Read the red callout above the table. Expect: Every kill command below is inert
documentation. Nothing on this page can run one — copy it into a terminal yourself if you
decide to act.

Search the table (Cmd+F) for keystone. Expect: ZERO matches. NOTES.md calls
com.google.keystone.agent and com.google.keystone.xpcservice the "leading safe-prune
candidates", but their only flag is unknown (empty plist, no purpose confirmed) — unknown
is not one of the five dispositions this table lists, so they correctly do NOT appear here.
"Named as worth investigating" and "flagged as prunable" are different claims; this table only
makes the second one. Confirm this by opening #/plate/s1-launchd-user/com-google-keystone-agent
directly instead (this is exactly F7's specimen).

Click any row's label link. Expect: it opens that item's Specimen view (covered in F7) —
rows are real links into the data, not static text.

Final check: in the Disposition column, confirm every row shows one of exactly five words —
orphaned, broken, duplicate, outdated, or dangling-risk — each followed by a plain-English
sentence, never a bare flag with no explanation.

F6

What starts itself

Proves the self-starting rollup (login/boot/schedule/keep-alive) is correctly classified across every surface at once.

Persona / precondition: on the Index Sheet.

0/4

Click What starts itself in the left rail (or go to #/starts). Expect: heading What
starts itself
, section marker V5, intro ending 68 of 528 items qualify.

Read the four subsection headers. Expect: At login (48), At boot (9), On a schedule
(10)
, Kept alive (restarted if it dies) (1) — in that order. (I recomputed this
classification myself from the embedded trigger strings using the page's own classifier logic;
all four numbers matched what's on screen.)

Expand the "Kept alive" section (should be exactly one row). Expect: a single item whose
Trigger column contains the word KeepAlive (case-insensitive) — the rarest class on the
machine.

Final check: in the right-hand legend, find "Self-starting, by owner". Expect: it breaks
the 68 items down by owner (third-party / wolf-project / apple) and each owner is a clickable
link into the filtered view for that owner.

F7

Specimen drill-down and the escape-hatch chain

Proves the deepest view (one item's full dossier) renders correctly and that Esc reliably backs out one level at a time, never dead-ending.

Persona / precondition: on the Index Sheet.

0/8

Navigate directly to ATLAS.html#/plate/s1-launchd-user/com-google-keystone-agent (paste into
the address bar after the already-open file/page, or search keystone from anywhere and click
the first com.google.keystone.agent result). Expect: breadcrumb Index Sheet /
s1-launchd-user / com.google.keystone.agent
, and the browser tab title updates to
com.google.keystone.agent — ATLAS.

Read the Kind/Trigger/Vendor/Purpose fields. Expect: Kind user-launch-agent, Trigger
manual, Vendor Google, and Purpose starting Unknown: this legacy Google Keystone-named
plist has no launch keys or executable in its parsed dictionary.

Read "Contains secrets". Expect: false.

Read the Kill command block. Expect: a red banner reading exactly Documentation only —
nothing on this page executes this
, above the command launchctl bootout
gui/501/com.google.keystone.agent && rm
/Users/wolf/Library/LaunchAgents/com.google.keystone.agent.plist
, with its own COPY button
and no run/execute affordance anywhere near it.

Press Esc. Expect: you land on the s1-launchd-user PLATE view (breadcrumb shrinks to
Index Sheet / s1-launchd-user) — one level up, not straight to the Index Sheet.

Press Esc again. Expect: you land on the Index Sheet.

Press Esc a third time, while already on the Index Sheet. Expect: nothing happens (no
error, no navigation) — Esc on the top-level view is a no-op, not a crash.

Final check: navigate to a made-up item, e.g.
ATLAS.html#/plate/s1-launchd-user/does-not-exist. Expect: a red callout: No item
does-not-exist on plate s1-launchd-user.
with a working Return to the Index Sheet. link — a
bad URL degrades gracefully, it doesn't blank the page.

F8

Cross-cutting search and filters

Proves the fast path (press / from anywhere) and the full filter view both work, including the honest zero-result case.

Persona / precondition: on any view.

0/6

Press / (not while focused in a text field). Expect: the masthead search box gains focus
immediately — no click needed.

Type chatcode and wait about a quarter second (the search debounces). Expect: you land on
#/filters?text=chatcode, heading Cross-cutting filters, results line reading 15 of 528
items across 12 plates
.

Clear the text field and type zzzzzz (a string that matches nothing on this machine).
Expect: results line 0 of 528 items across 12 plates, and the table body shows No items
match the current filters.
— not a blank white table.

Clear the search, then click one owner chip (e.g. apple) and one flag chip (e.g.
security-sensitive). Expect: an "active filters" row of pills appears (owner: apple,
flag: security-sensitive) plus a Clear all button, the results count updates to reflect
both filters applied together (AND across categories), and the URL hash updates to reflect
exactly the active filters.

Click Clear all. Expect: all pills disappear and the count returns to 528 of 528 items
across 12 plates
.

Final check: press Esc while filters are still active from step 4 (redo it if you already
cleared them). Expect: Esc clears the active filters in place — you stay on the filters view,
you don't get bounced to the Index Sheet, which is the one case where Esc means "clear" instead of
"go back".

F9

One plate in depth: S6 containers

Proves a single-surface page renders its full contents (coverage note, scan commands, item table, dossier prose) and specifically that the container surface now shows zero persistent containers — consistent with the one deliberate machine change this project made.

Persona / precondition: on the Index Sheet.

0/6

Click plate VI s6-containers. Expect: breadcrumb Index Sheet / s6-containers, a large
VI numeral, and the line generated July 25, 2026, 3:11 AM · 20 items · 0 live (the time
assumes your Mac is on America/New_York time, per the caveat at the top of this document).

Read the coverage note. Expect: it states the bounded docker ps -a scan found containers
that are all ephemeral terminus code-review sandboxes and that none of them is itemized — this
surface intentionally itemizes only persistent things (images, networks, runtime-presence
checks), not the sandbox churn.

Scroll the item table. Expect: 8 image rows, 7 network rows, and 5
runtime-presence rows (20 total) — and NOT a single row of kind container. Confirm no row's
label contains speedreader or postgres.

Scroll to "Scan commands". Expect: a list including docker info >/dev/null 2>&1; echo $?
and docker system df verbatim.

Scroll to "Dossier". Expect: rendered prose (not raw markdown asterisks/hashes) describing
the surface story in full sentences.

Final check: click a column header in the item table (e.g. "Owner"). Expect: the table
re-sorts and an aria-sort state changes on that header — table columns are genuinely sortable,
not static.

F10

Run the fleet-status apparatus (`_build/status.py`) — exposes a real defect

Proves the README's own documented command runs cleanly — and surfaces a genuine mismatch between what it reports and what the shipped atlas actually contains.

Persona / precondition: a terminal, cd ~/Projects/mac-atlas, python3 on PATH.

0/5

Run python3 _build/status.py. Expect: a table, one row per surface s1s13, with
columns items, live, state; all twelve scan surfaces (s1s12) show state PROMOTED
and s13-atlas shows queued; a final line TOTAL (in-unit) with promoted surfaces: 12/12.

Compare the s6-containers row's item count against what F9 step 1 showed you inside
ATLAS.html. KNOWN DEFECT, currently reproduces: status.py reads
units/s6-containers/inventory.yaml (19 items) — NOT the promoted
inventory/s6-containers.yaml that the shipped ATLAS.html was actually built from (20 items).
Correct behavior would be: these two numbers agree, since status.py labels the row
PROMOTED — implying "this is what shipped" — while actually reading a different, later-edited
file. Currently it shows 19, not 20.

Same check for s10-agent-infra: status.py reports 16; the shipped atlas (F2 step 4 plate
tile, or #/plate/s10-agent-infra) shows 17. Same root cause, same defect.

All other ten surfaces (s1s5, s7s9, s11s12). Expect: status.py's counts
agree with the shipped atlas's plate tiles, since for those surfaces the units/ working copy
and the promoted copy happen to still match.

Final check: this is a display defect, not a data-integrity one — git diff-style, it means
status.py can silently under- or over-report a surface once someone re-runs a scan into units/
without re-promoting it. The shipped ATLAS.html itself is unaffected (it always reads the
promoted copies). Confirm this by re-reading inventory/s6-containers.yaml directly and checking
it has 20 items — the source of truth for what actually shipped.

F11

Run the drift sweep against the live machine

Proves the project's central honesty claim for real: that a snapshot names exactly what has changed since it was taken, rather than silently going stale.

Persona / precondition: a terminal, cd ~/Projects/mac-atlas. Read-only — this makes no changes to your Mac; it only runs enumeration commands and diffs their output against the promoted YAML.

0/4

Run bash _build/drift-sweep.sh. Expect: it exits non-zero (currently exit code 1) and
ends with a line SWEEP DRIFT — reconcile the surfaces above (add the named items), then
re-run
. A clean machine would instead print SWEEP CLEAN — every promoted surface still
matches the machine
and exit 0 — both outcomes are legitimate; what matters is that the
script always tells you which one you got.

Read the summary lines near the end. As of this writing: in parity : 5 naming
s2-launchd-system s3-btm-loginitems s4-cron-periodic s7-applications s9-user-automations, and
drifted : 7 naming s1-launchd-user s10-agent-infra s11-project-scripts s12-network-listeners
s5-homebrew s6-containers s8-shell-env
. These exact names will differ by the time you run
this
— the machine keeps moving, which is the entire point — but every surface that drifted
must be individually NAMED here, never just counted.

Pick one drifted surface from the output (e.g. s1-launchd-user) and read its lines.
Expect: each failure names the SPECIFIC label(s) involved — e.g. missing-from-inventory=[…]
or not-on-machine=[…] — never a bare "counts don't match". This is the set-based parity check
NOTES.md describes fixing after an earlier count-only version flaked on a live machine.

Final check: re-run the same command a second time immediately. Expect: the reported drift
is stable (same surfaces, same or a strict superset of named items) — a positive result should be
reproducible, not a coin flip.

F12

Gate a single surface (the healthy path)

Proves the per-surface completion gate genuinely runs its full checklist and passes cleanly on a surface that hasn't drifted — the positive-path counterpart to F11.

Persona / precondition: a terminal, cd ~/Projects/mac-atlas.

0/4

Run python3 _build/atlas-gate.py units/s7-applications. Expect: a run header
[<timestamp>] s7-applications followed by fourteen lines in this exact order:
yaml-parses, header-surface, header-generated, header-scan_commands,
header-coverage_note, items-nonempty — 50 items, ids-unique, schema-complete,
owner-enum, evidence-nonempty, kill-documented,
parity-user-facing-application-count — missing-from-inventory=none not-on-machine=none,
dossier-substantive, secrets-clean.

Read the line below the checks. Expect: · unknowns: 4 naming markchart,
pages-creator-studio, iterm-browser-plugin, macos-instantview — an honest unresolved list,
not zero unknowns pretending everything was identified.

Read the final line. Expect: ATLAS-GATE PASS.

Final check: run python3 _build/atlas-gate.py units/does-not-exist (a made-up path).
Expect: it fails loudly and immediately — GATE FAIL: no spec at
/Users/wolf/Projects/mac-atlas/specs/does-not-exist.yaml
— with a non-zero exit code, never a
silent pass on a surface that was never scanned.

F13

What I could not verify myself

Honest gaps: things a human needs a real device or a longer clock to check, which this document cannot close by reading source code.

Persona / precondition: your own phone or tablet, and patience for the second half.

0/4

Open ATLAS.html on a phone-width screen (either genuinely on a phone, or by narrowing a
desktop browser window below ~560px). Expect (unverified by me — confirm yourself): no
horizontal body scroll, the masthead search and survey-age pill remain usable, and the left
rail either collapses to a usable control or stacks above the content instead of clipping off
the visible area. I could not drive a real narrow viewport for this session — the automation
layer I had access to renders at a fixed desktop width — so treat this entire step as
unconfirmed rather than passing by default.

In your OS or browser accessibility settings, turn on "reduce motion", then reload the atlas
and click through a few plates. Expect: table rows appear immediately at full opacity with
no fade/slide entrance, and the route-title crossfade on navigation is skipped (instant text
swap instead). This is code-verified — the stylesheet defines
table.data tbody tr.enter{opacity:1 !important} inside a
prefers-reduced-motion: reduce block, and morphRouteTitle checks the same media query before
animating — but I did not toggle the OS setting and watch it live myself.

Leave the page open and idle in a background tab overnight, then bring it to the foreground the
next morning without reloading. Expect: the survey-age pill's relative text (stale, N days
ago
) has advanced on its own — the page re-renders it every 60 seconds via a timer — without
needing a manual refresh. I did not run this overnight; confirm on your own schedule.

Final check: none of the three items above should be marked pass on my say-so alone — each
needs your own hands, which is the entire reason this flow exists.

Not covered and why

  • The full 12-plate walk. F9 exercises one plate (s6-containers) in depth as a

representative; the other eleven share the same template and are lower-risk to skip individually.
If any single plate looks broken, treat it as a new defect, not an F9 regression.

  • _build/refresh-atlas.sh end-to-end (actually regenerating ATLAS.html). F3 verifies the

page hands you the right command; running it would produce a NEW file with new totals, which
would immediately invalidate every exact number in F2, F4, F5, F6, and F9 above. Run it
deliberately, on its own, then re-derive this document against the new file.

  • **_build/gate.sh, _build/assemble-atlas.py, _build/build-atlas-data.py, dispatch.sh, and

the rest of _build/.** These are the fleet-construction apparatus, not something a human runs
to USE the deliverable — F10–F12 cover the three commands README.md actually surfaces to a
human (status.py, drift-sweep.sh, and atlas-gate.py as the re-gate example).
assemble-atlas.py is the one that would need touching if the status.py defect in F10 gets
fixed (or status.py itself, to read inventory/ instead of units/*/inventory.yaml).
python3 _build/build-atlas-data.py (recompile from promoted inventories) was not run — it
would produce a new atlas-data.json, another step toward a rebuild, same caveat as above.

  • The sudo-gated captures (_build/sudo-capture.sh, sudo lsof -i -P). Per Q-POSTURE these

are Wolf-fired only; this document sticks to the read-only lane a tester can run unattended.

  • The dossiers and inventory YAML as documents in their own right (dossiers/*.md,

inventory/*.yaml). Their content surfaces inside ATLAS.html (F9 step 5) and is exercised
there; reading the raw files directly is redundant with that.

Results

FlowVerdictFailures → tickets
F1 open cold
F2 index sheet numbers
F3 survey-age panel
F4 attack surface
F5 prune candidates
F6 what starts itself
F7 specimen + escape hatches
F8 search and filters
F9 plate in depth (S6)
F10 status.pyknown defect, see below
F11 drift-sweep.sh
F12 atlas-gate.py
F13 unverified gaps
updated just nownext 3m 00s