Published release verification
Published release verification
Before the release is cut — the cumulative upgrade gate
This section is the only part of this document that runs before a tag exists. Everything below it observes a published installer; this observes what that installer will do to a database someone already has.
A release that changes SCHEMA_VERSION upgrades every existing user in one
launch. v2.2.0 is the first to move a real corpus five steps at once (13 → 18),
and each of those five steps had a green test that started from the step before
it — none from a database a released resmon wrote.
Gate: resmon_scripts/verification_scripts/test_cumulative_upgrade.py is
green. It replays verification_scripts/fixtures/v2.1.0/corpus_schema_13.sql,
a corpus produced by tag v2.1.0’s own code, and calls today’s init_db once.
It runs in the ordinary hermetic suite on every PR, so in practice this is a
check that it was collected rather than a separate command:
.venv/bin/python -m pytest -q resmon_scripts/verification_scripts/test_cumulative_upgrade.py
One case per released fixture regenerates it from that release’s own tag and
diffs it against the committed file. CI fetches those tags explicitly (the
“Fetch the released tags” step in .github/workflows/ci.yml) and sets
RESMON_REQUIRE_V210_TAG=1, RESMON_REQUIRE_V220_TAG=1 and
RESMON_REQUIRE_V230_TAG=1, so in
CI a missing tag is a failure, not a skip. Elsewhere — a shallow clone, a source
tarball — the case skips; with -rs a local run says which happened:
git fetch --tags upstream
.venv/bin/python -m pytest -q -rs \
resmon_scripts/verification_scripts/test_cumulative_upgrade.py \
-k the_committed_fixture_is_what
The rule for every release after this one. A release that ships a new
SCHEMA_VERSION adds the fixture of the version it ships, so the next release
walks from it. The fixture is committed in the release pull request, and it
is finished off after the tag — because
test_the_release_that_shipped_this_schema_left_a_fixture falls due on the
commit that bumps APP_VERSION, which is inside that pull request, and the tag
does not exist until it merges. v2.2.0’s fixture was generated from its tag only
because its schema had already shipped; that is not the general case.
In the release PR:
- Run the generator against a disposable worktree of your own release branch
head (the version-bump commit or later) —
python resmon_scripts/verification_scripts/fixtures/<tag>/generate_corpus.py --source-tree <that worktree>— seeding every table that version has, at every value of every CHECK-constrained column it has. Where a value is in a CHECK’s vocabulary and nothing in the release writes it, leave it out and name it in the generator with the reason; a fixture that invents a state the release cannot produce is worse than one that is honestly incomplete. - Commit the dump beside the generator. Its header names the branch head the dump was generated from, says that commit is ahead of the tag, and says what the follow-up replaces. The dump body depends on the shipped schema’s code alone, which the squash commit and the tag carry unchanged; only the hash differs.
- Add the row to
RELEASEDintest_cumulative_upgrade.py, with itsrequire_envswitch. Leave that switch out of.github/workflows/ci.yml’s tag-fetch step: the fetch would fail on a tag that does not exist yet, and the new release’s provenance case is expected to skip until the follow-up. Say so in the PR body — an unexplained skip and a hidden hole look the same. - Check that the cumulative walk case is parametrised over every row of
RELEASED, not only the newest. The walk that matters is the last released version to the one about to ship, and a case pinned to “the newest” drops exactly that walk on the commit that adds the new fixture.
In the post-tag follow-up, once the tag is published:
- Regenerate from a disposable worktree of the tag itself, replace the header’s commit hash, and confirm nothing else in the dump moved. A body that changes here means something other than the schema’s code fed the dump, and that is the finding, not a nuisance.
- Add the new tag to the tag-fetch step in
.github/workflows/ci.ymland set itsrequire_envthere, which turns the case’s “tag is not here” skip into a failure. Without this the case skips in CI and the new fixture is unguarded — prove the step by altering one fixture row on that PR and watching the Backend jobs go red, then revert it.
Skipping step 1 for a release costs nothing that release and leaves the next one with no way to walk from a real database. That is how five untested steps accumulated.
Applied for v2.2.0. fixtures/v2.2.0/corpus_schema_18.sql and its generator
are committed and every step above is done for it, including the CI tag fetch
and its required switch.
Applied for v2.3.0. fixtures/v2.3.0/corpus_schema_21.sql and its generator
are committed in the release PR, generated from the release branch head, with
the header saying so. Steps 5 and 6 were applied on 2026-09-21, once tag v2.3.0
existed: the dump was regenerated from a disposable worktree of the tag, which
replaced the header’s commit hash and moved no other line, and ci.yml now
fetches v2.3.0 beside the other two and sets RESMON_REQUIRE_V230_TAG, so
test_the_committed_fixture_is_what_that_releases_own_code_produces[v2.3.0]
fails in CI rather than skipping. Two enumerated
CHECK values are deliberately unseeded and named in the generator’s
UNWRITTEN_CHECK_VALUES: routine_missed_fires.disposition = 'skipped', which
database.py says on the DDL that nothing writes yet, and
routines.execution_location = 'cloud', which insert_routine refuses and
init_db rewrites — a row of it would make this fixture’s no-op walk false.
Because nothing migrates past 21 yet, the walk it serves today is 21 → 21; the
18 → 21 walk a 2.2.0 user actually takes runs over the v2.2.0 fixture, which
this release does not touch.
Procedure
This procedure observes a published installer on the host that runs it. It does not establish that another platform’s installer launches, that every control works, or that a release should ship. Record failures and unverified properties without repairing the application as part of this check.
- Start in the authorized checkout. Run
pwdbefore each Git command, inspect status and remotes, fetchupstream, and create acodex/branch fromupstream/main. Record the exact baseline commit and any change from the delegation’s baseline. Leave unrelated checkouts and existing applications alone. - Create a uniquely named scratch directory outside the checkouts, with separate
assets,arm64,x64, andevidencesubdirectories. Use explicit repository arguments:gh release view <tag> --repo ryanjosephkamp/resmon --json tagName,url,publishedAt,assetsandgh release download <tag> --repo ryanjosephkamp/resmon --dir <scratch>/assets. Preserve the metadata; compare each downloaded file’s byte size (stat -f '%z' <file>on macOS) with its release-asset size. Record the actual asset inventory, not an assumed count. Do not infer launchability from sizes. - Before launching, inspect the matching checkout’s Electron startup code.
tryAttachToDaemon()readsdaemon.lockunderstateDir()and can contact that port even when the daemon’s version differs. A normal launch on a machine with a live daemon is therefore not isolated. Where contacting port 8742 is prohibited, do not perform an HTTP health check or launch with the default state directory. Capturelsof -nP -iTCP:8742 -sTCP:LISTENbefore and after; this inspects the listener without connecting. It cannot establish unchanged HTTP health, database contents, or uninterrupted service. - Inspect the downloaded DMG’s extended attributes with
xattr -l, mount it read-only withhdiutil attach -readonly -nobrowse, and usedittoto copy its.appinto the architecture’s scratch subdirectory, never/Applications. For example: `hdiutil attach -readonly -nobrowse -mountpoint/mount-arm64 /assets/resmon- -arm64.dmg`, then `ditto /mount-arm64/resmon.app /arm64/resmon.app`. Preserve attributes. Inspect the copied application's attributes and `Contents/Info.plist`; record the download method and quarantine state. Do not remove quarantine, re-sign the bundle, or change system security policy. -
Launch the copied published arm64 app through LaunchServices with a fresh state directory and separate Electron user data. For example, after creating the directories, run the following with absolute paths substituted:
open -n -a <scratch>/arm64/resmon.app \ --env RESMON_STATE_DIR=<scratch>/arm64/state \ --stdout <scratch>/evidence/arm64-stdout.log \ --stderr <scratch>/evidence/arm64-stderr.log \ --args --user-data-dir=<scratch>/arm64/electron-user-dataDo not set
RESMON_PYTHONto a checkout interpreter or enableRESMON_E2E: the published bundle and its ordinary renderer behavior are the subject. Confirm the startup log identifies an ephemeral backend port other than 8742. Record this isolated-state launch as a controlled variation of the ordinary Finder path. This isolates the database and Chromium profile, not the OS keyring or system service installation. Opening Cloud Storage may probe an existing Drive credential; do not link, unlink, save keys, or invoke backup as part of a route walk. If macOS blocks launch, capture its exact message. A Finder right-click → Open retry must preserve isolation; do not retry in a way that loses the state-directory override. If that cannot be guaranteed, record the retry as unverified. Do not claim that an unquarantined command-line download exercised the browser-download Gatekeeper path. -
Derive the route list by evaluating
allRouteHashes()fromresmon_scripts/frontend/src/routes.tsin the recorded checkout. Include parent routes and every declared child, including redirecting parents. Walk each in the published application’s window, wait for its content, and save one numbered screenshot and one observation per route. State separately the number of rendered route entries, the brief’s expected count, and the count derived from the file. A visible page shell is not verification of every control, external embed, or workflow on that page.With the checkout’s frontend dependencies installed, this prints the route denominator without launching either app (run from
resmon_scripts/frontend):node - <<'JS' const fs = require('fs'), ts = require('typescript'); const source = fs.readFileSync('src/routes.ts', 'utf8'); const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS } }).outputText; const routeExports = {}; new Function('exports', js)(routeExports); const routes = routeExports.allRouteHashes(); console.log(JSON.stringify({ count: routes.length, routes }, null, 2)); JS -
On Repositories, record the source count actually displayed. On About resmon → About App, record the displayed version. Record visible errors and any console observations with their collection limits. Do not expose credentials or existing personal data in public evidence. For releases with first-run onboarding, capture all step details before any action. Use one fresh state/profile to test Skip followed by reload, and a second fresh state/profile to test first-routine retirement without Skip. Record the actual initial route: if ordinary startup opens Dashboard, do not call a later visit the first opening or claim the card never appeared. Create the routine through the UI, record its initial status, deactivate it if needed, and revisit/reload Dashboard. Keep the precise requested sequence unverified when the launch path cannot provide it.
For an assistant check, use the published panel and the installed CLI. Record a read-only question’s tool call, answer, cards (if any), and cost line. Ask for activation of an isolated inactive routine, capture any approval card, deny it, then reload Routines to inspect the state. Never approve an assistant write in this procedure. If sign-in is missing, record the exact panel message and stop that portion. If a turn fails or times out, retain the displayed message and distinguish absent evidence from a successful read or denied write; an absent cost line is not a zero-cost measurement.
- Quit arm64 before repeating the mount, copy, isolated launch, version check, and bounded observation with the x64 DMG. Record whether Rosetta is available and whether the x64 app launches. If unavailable, report that limit; do not install Rosetta or modify the machine to make this check pass. Unless a second full route walk was performed, do not claim one for x64.
- State which other platforms were not launched and why. On this Mac, Windows and Linux evidence is limited to the downloaded asset names and byte sizes.
- Quit only the applications started for this run. Check their recorded process
IDs have exited and repeat the non-contact listener observation. Detach only
the volumes mounted for this run. Preserve reviewed screenshots and receipts
outside the repository, attach them to the PR, verify the attachment links,
then remove the run’s downloaded installers, copied apps, and temporary state.
Use
hdiutil detach <scratch>/mount-arm64and the corresponding x64 mountpoint; do not detach unrelated pre-existing volumes. - Run the repository’s required checks and record exact commands, counts,
failures, skips, and environmental limitations. Keep their locally built app
evidence distinct from the published-installer walkthrough. Commit only this
document, push the branch according to
AGENTS.md, and open one PR againstryanjosephkamp/resmon. Its body records what changed, verification, deliberate limits, and any files outside the brief. Screenshots are PR attachments, never committed files.
v1.8.7 run
Subject and launch conditions
Observed on 2026-09-05, macOS 26.3.1 (a), build 25D771280a, arm64 host.
The subject was the published v1.8.7 release,
published at 2026-09-04T21:18:46Z. The checkout and fetched upstream/main were
30658b5 (release: 1.8.7 — a transport failure is not an answer (#73)), exactly
the brief’s baseline. The branch is codex/docs-release-launch-check; the Codex
prefix follows AGENTS.md rather than the brief’s example docs/ branch.
Assets were downloaded with gh release download v1.8.7 --repo
ryanjosephkamp/resmon --dir /private/tmp/resmon-release-check-20260905/assets.
Both DMGs were mounted read-only and their apps copied with ditto into that
scratch directory. No local build was substituted for either published app.
The arm64 bundle’s app.asar was also read to confirm it honors the state-directory
override and otherwise probes the daemon lock’s port.
Both LaunchServices launches used open -n -a <copied-app> with the procedure’s
--env RESMON_STATE_DIR, --stdout, --stderr, and --user-data-dir arguments;
x64 additionally used --arch x86_64. Neither enabled RESMON_E2E nor supplied
RESMON_PYTHON. The apps used their bundled backend interpreters. Native UI
automation clicked the sidebar and tab links and captured the application window.
Asset inventory
All seven downloaded sizes equaled their GitHub release metadata sizes. The table reports byte counts, not estimates from the rounded release-page labels.
| Asset | Release bytes | Downloaded bytes |
|---|---|---|
latest-linux.yml |
378 | 378 |
latest.yml |
349 | 349 |
resmon-1.8.7-arm64.dmg |
221,085,262 | 221,085,262 |
resmon-1.8.7-setup-x64.exe |
194,649,899 | 194,649,899 |
resmon-1.8.7-setup-x64.exe.blockmap |
203,931 | 203,931 |
resmon-1.8.7-x64.dmg |
222,970,638 | 222,970,638 |
resmon-1.8.7-x86_64.AppImage |
250,603,195 | 250,603,195 |
The two DMG SHA-256 values also matched the release asset digests:
- arm64:
bc54e77dd967ccec59e23095c2fbe9f93ea97d00b37c43c0ce5b1d40b17c9c2d. - x64:
4fcc758b59a733690cbf058336d25b292f5512b02b77d585aefc0930c9adfa0c.
Windows and Linux were not launched: this run used a macOS host and no Windows or Linux execution environment. No claim about those installers is made beyond their names and sizes. Updater-feed contents and update installation were not verified in this run.
Gatekeeper observation
No Gatekeeper warning or confirmation dialog was observed on either controlled
LaunchServices launch. xattr -l showed com.apple.provenance and no
com.apple.quarantine attribute on each downloaded DMG and copied app root.
No attribute was removed or added, no bundle was re-signed, and system security
policy was not changed.
Consequently the brief’s assumed first-launch block did not occur. The quarantined browser-download / Finder right-click → Open path remains unverified. A plain Finder retry would also lose the per-launch state override and could probe the prohibited daemon. The result is an isolated launch of the published bundle, not proof of the unmodified Finder/Gatekeeper path.
Arm64 route walk
24 of the brief’s 24 route entries rendered; 24 entries were derived from
allRouteHashes() in the checkout’s routes.ts. The Settings and About parent
entries were visited separately from their redirect targets. Screenshots are
numbered 01–24 in the PR evidence archive; each is one window viewport, not a
full-page capture. No searches, scheduled runs, credential changes, destructive
actions, test emails, or backups were initiated.
| # | Requested route | Observation | Screenshot |
|---|---|---|---|
| 1 | / |
Welcome content; no active routines or recent activity. | 01-dashboard.png |
| 2 | /dive |
Repository/date/keyword form and Run Deep Dive control rendered. | 02-deep-dive.png |
| 3 | /sweep |
Multi-source form rendered; selector reads “0 of 25 selected.” | 03-deep-sweep.png |
| 4 | /routines |
Routine table rendered with “No routines configured.” | 04-routines.png |
| 5 | /calendar |
September 2026 grid rendered; previous/next controls show square glyphs. | 05-calendar.png |
| 6 | /results |
Filters and execution table rendered with “No executions found.” | 06-results.png |
| 7 | /analytics |
“Nothing to analyze yet” panel rendered. | 07-analytics.png |
| 8 | /watchdog |
Empty-history and unchecked-corpus notices rendered. | 08-watchdog.png |
| 9 | /explorer |
Filters and “Nothing collected yet” panel rendered. | 09-explorer.png |
| 10 | /configurations |
Routine Configs table rendered with “No configurations.” | 10-configurations.png |
| 11 | /monitor |
“No active executions” panel rendered. | 11-monitor.png |
| 12 | /repositories |
Attributions and source table rendered; 25 source rows counted from its accessibility tree. | 12-repositories.png |
| 13 | /settings |
Redirected to /settings/email; email form rendered. |
13-settings.png |
| 14 | /settings/email |
Email form rendered; no email was sent. | 14-settings-email.png |
| 15 | /settings/cloud |
After loading, displayed “Linked — API unreachable (RefreshError)”; Backup Now disabled. | 15-settings-cloud.png |
| 16 | /settings/ai |
Provider/key-status form rendered; detected an installed Claude Code command and selected its lane. | 16-settings-ai.png |
| 17 | /settings/storage |
Retention controls, explanatory note, and export location rendered. | 17-settings-storage.png |
| 18 | /settings/notifications |
Notification choices and “Current: granted” permission status rendered. | 18-settings-notifications.png |
| 19 | /settings/advanced |
Service/concurrency/diagnostics/danger-zone controls rendered; “no daemon running” concerns the isolated state directory. | 19-settings-advanced.png |
| 20 | /about-resmon |
Redirected to /about-resmon/tutorials; contents and overview rendered; the captured video area was black. |
20-about-resmon.png |
| 21 | /about-resmon/tutorials |
Tutorial contents and an overview video thumbnail rendered; playback untested. | 21-about-resmon-tutorials.png |
| 22 | /about-resmon/issues |
Issue form rendered; nothing submitted. | 22-about-resmon-issues.png |
| 23 | /about-resmon/blog |
After loading, post list and embedded resmon Blog page rendered. | 23-about-resmon-blog.png |
| 24 | /about-resmon/about-app |
Version reads 1.8.7; also shows “Current release line: 1.5.x” and Update 7 prose. | 24-about-resmon-about-app.png |
The Repositories count is a count of the 25 rendered source-row buttons, not
a total badge displayed by that page. repository-rows.txt in the evidence archive
lists them. The Deep Sweep screenshot independently displays the selector’s total.
This does not verify that any source search succeeds.
The initial database and profile were fresh, but the keyring was shared with the
host. Cloud Storage’s automatic connection probe found an existing credential and
reported RefreshError. It was not linked, unlinked, or repaired during this run.
Console and visible discrepancies
After the arm64 walkthrough, the main renderer’s DevTools Console contained one
error and one warning, captured in 25-arm64-console.png:
- A
data:application/x-font-ttffont was blocked bydefault-src 'self', with the console explaining thatfont-srcwas not explicitly set. Unrecognized feature: 'web-share'.
The Calendar screenshot shows square navigation glyphs. That observation and the font error are both recorded; this run did not diagnose their causal relationship. About App’s version is 1.8.7 while its separate release-line sentence says 1.5.x. Neither discrepancy was repaired or assigned release-blocking severity.
The Blog webview’s separate Console displayed zero messages when inspected.
This was a post-walk console inspection, not continuous per-route console capture;
it does not establish that every renderer, iframe, or discarded webview was
error-free. Startup stderr recorded [DEP0180] DeprecationWarning: fs.Stats
constructor is deprecated on both architectures. Arm64 also recorded
Google Drive connection check failed: RefreshError. One intermediate log read
showed sysmon request failed with error: sysmond service not found; it was not
present in the later complete stderr read and is retained here as an observation,
not attributed to a diagnosed application defect.
x64 / Rosetta result
file identified the copied x64 executable as Mach-O 64-bit executable x86_64.
pkgutil --pkg-info com.apple.pkg.RosettaUpdateAuto found an installed Rosetta
receipt, and open --arch x86_64 launched the app on this arm64 host. The Dashboard
rendered with Backend Online, and About App displayed 1.8.7. Evidence:
26-x64-dashboard.png and 27-x64-about-app.png. A full x64 route walk was not
performed. No Rosetta installation was needed. A native automation lookup initially
timed out on the /private/tmp spelling; selecting the running bundle’s registered
/tmp path resolved it without changing the app.
Isolation and cleanup
Arm64 launched app PID 25852, bundled backend PID 25864, backend port 56875, and renderer port 56882. x64 launched app PID 27087, bundled backend PID 27137, backend port 58176, and renderer port 58205. The task’s four recorded app/backend PIDs were absent after quitting both apps. Both task-mounted DMGs were detached. Downloaded assets, copied apps, and temporary state were removed after preserving evidence outside the repository.
Before and after, lsof -nP -iTCP:8742 -sTCP:LISTEN showed the same Python listener:
PID 1150, FD 12u, socket identifier 0xaa26ce450f50da0e, address
127.0.0.1:8742. The task did not bind or connect to that port, stop that process,
or edit its lock. The explicit user prohibition took precedence over the brief’s
conflicting before/after curl instruction. P3’s requested HTTP-health proof is
not established; these snapshots only establish unchanged listener identity at
the two observation times. There was no packet trace or continuous health monitor.
Repository checks, distinct from the release walk
| Command | Observed result |
|---|---|
.venv/bin/python -m pytest -q |
1,030 passed, 45 deselected, 59.43 s with local socket access. |
npm run typecheck |
Exit 0. |
npm test -- --runInBand |
190 passed, 26 suites passed. |
npm run build |
Exit 0; renderer and Electron builds completed. |
npm run e2e |
58 passed, 2 skipped, 2.0 min on the macOS display. |
Frontend commands ran from resmon_scripts/frontend; pytest ran from the checkout
root. The first sandboxed pytest attempt returned 1,012 passed, 4 failed, 14 errors,
45 deselected because loopback socket operations were denied. The rerun above used
local socket access and passed without source changes.
The e2e run skipped the local packaged-app launch because the existing local
bundle was 1.8.4, not the checkout’s 1.8.7, and skipped the strict no-CLI
environment case because a real Codex command was detected. It also printed
P13c MONITOR NOT VERIFIED: a no-HTTP search completed before the active-execution
poll could adopt it, leaving zero zero-reason Monitor rows. These limitations are
not replaced by the published-app screenshots. The 45 live-network backend tests,
video playback, actual source searches, AI calls, notifications, email, backup,
updating, and Windows/Linux execution were not verified by this release walk.
Evidence and scope
The PR evidence archive is resmon-v1.8.7-launch-screenshots.zip: 27 native-window
PNGs, a 24-route screenshot/hash manifest, the rendered source-row list, the asset
size/DMG-digest receipt, and before/after listener snapshots. Screenshots were
reviewed as window captures; they are not a pixel-complete design or accessibility
audit. No screenshots are committed. The sole repository change is this document;
no application code, dependencies, workflows, or other documentation changed.
v2.0.1 run
Subject and launch conditions
Observed on 2026-09-06 (America/New_York; 2026-09-07 UTC), macOS
26.3.1 (a), build 25D771280a, arm64 host. The subject was
the published v2.0.1 release,
published at 2026-09-06T22:05:42Z. The checkout baseline was
055c4f9f9930811af871e381ff88ca986611c960 (current upstream/main, including
PR #98); branch codex/release-launch-v2. This is the checkout used to derive the
route denominator, not a claim that every change on that baseline is in the DMG.
All release assets were downloaded with gh release download v2.0.1 --repo
ryanjosephkamp/resmon --dir /tmp/resmon-v2-launch-20260906/assets. Both DMGs
were mounted read-only and the apps copied with ditto outside both checkouts.
The published arm64 app.asar was read to confirm RESMON_STATE_DIR and the
daemon-lock lookup before launch. No checkout interpreter or local build was
substituted for the published bundle.
LaunchServices used the procedure’s open -n -a, --env RESMON_STATE_DIR,
--stdout, --stderr, and --user-data-dir arguments. Every launch had new
backend state and a new Chromium profile. Neither RESMON_E2E nor
RESMON_PYTHON was supplied. Native accessibility actions operated the app’s
sidebar, tabs, forms, and assistant; screenshots are individual window viewports.
Asset inventory
All seven downloaded names and byte sizes matched the release metadata. SHA-256 was computed for all seven files and matched GitHub’s asset digests.
| Asset | Release bytes | Downloaded bytes |
|---|---|---|
latest-linux.yml |
378 | 378 |
latest.yml |
349 | 349 |
resmon-2.0.1-arm64.dmg |
221,270,814 | 221,270,814 |
resmon-2.0.1-setup-x64.exe |
194,928,665 | 194,928,665 |
resmon-2.0.1-setup-x64.exe.blockmap |
204,366 | 204,366 |
resmon-2.0.1-x64.dmg |
223,212,602 | 223,212,602 |
resmon-2.0.1-x86_64.AppImage |
250,853,419 | 250,853,419 |
DMG SHA-256:
- arm64:
9f893f842291d4f551275cec1d187d6b43291d079ad50d45336d6e95fe2591e9. - x64:
6fb67b0da5885f3c5ba2c25ee803b484746342dcc49cdf4a5477bde67c1c061b.
Windows and Linux were not launched: this run had a macOS host and no Windows/Linux execution environment. Size and digest agreement does not establish launchability. Updater contents and update installation were not exercised.
Gatekeeper observation
xattr -l showed com.apple.provenance, with no com.apple.quarantine, on both
DMGs and copied app roots. No Gatekeeper warning appeared on the controlled
launches. No attributes were removed, bundles re-signed, or security settings
changed. The quarantined browser-download / Finder right-click → Open path
remains unverified.
Arm64 route walk
24 of 24 route entries rendered, with 24 derived by evaluating
allRouteHashes() from the baseline’s routes.ts. The brief requires every route
rather than stipulating a numeric count. Both redirecting parents were clicked
separately from their child tabs. This establishes the visible content in these
viewports, not every control or workflow on each page.
| # | Requested route | Observation | Screenshot |
|---|---|---|---|
| 1 | / |
Welcome, three-step Getting started card, empty activity. | 01-dashboard-first-run.png |
| 2 | /dive |
Repository/date/keyword form and Run control. | 02-deep-dive.png |
| 3 | /sweep |
Multi-source form; “0 of 25 selected.” | 03-deep-sweep.png |
| 4 | /routines |
“No routines configured” before creating the task routine. | 04-routines.png |
| 5 | /calendar |
September grid; previous/next chevrons visibly render. | 05-calendar.png |
| 6 | /results |
Filters and “No executions found.” | 06-results.png |
| 7 | /analytics |
“Nothing to analyze yet.” | 07-analytics.png |
| 8 | /watchdog |
Empty-history notice and unchecked-corpus explanation. | 08-watchdog.png |
| 9 | /explorer |
Filters and “Nothing collected yet.” | 09-explorer.png |
| 10 | /configurations |
Routine Configs table contains the newly created task routine. | 10-configurations.png |
| 11 | /monitor |
“No active executions.” | 11-monitor.png |
| 12 | /repositories |
Attributions and 25 source-row buttons in the accessibility tree. | 12-repositories.png |
| 13 | /settings |
Redirected to Email; form rendered. | 13-settings-parent.png |
| 14 | /settings/email |
Email form; no send or save action. | 14-settings-email.png |
| 15 | /settings/cloud |
“Linked — API unreachable (RefreshError)”; Backup Now disabled. | 15-settings-cloud.png |
| 16 | /settings/ai |
Key-status table and automatically selected Claude Code lane. | 16-settings-ai.png |
| 17 | /settings/storage |
Retention note and export-location controls. | 17-settings-storage.png |
| 18 | /settings/notifications |
Choices and “Current: granted.” | 18-settings-notifications.png |
| 19 | /settings/advanced |
Installed service, isolated window PID, no active scheduler jobs. | 19-settings-advanced.png |
| 20 | /about-resmon |
Redirected to Tutorials; contents and video thumbnail rendered. | 20-about-parent.png |
| 21 | /about-resmon/tutorials |
Contents and video thumbnail; playback not tested here. | 21-about-tutorials.png |
| 22 | /about-resmon/issues |
Issue form; nothing submitted. | 22-about-issues.png |
| 23 | /about-resmon/blog |
Post list and embedded published blog loaded. | 23-about-blog.png |
| 24 | /about-resmon/about-app |
Version 2.0.1, release line 2.0.x. | 24-about-app.png |
The 25-source count is from rendered row buttons, not a total badge on
Repositories. repository-rows.txt records the list; Deep Sweep independently
shows its selector total. No source searches were run in the published app.
Cloud Storage probed an existing system-keyring credential automatically and
reported RefreshError. No credential, backup, or service-installation controls
were changed. Advanced’s “no daemon running” describes the isolated state and is
not an observation about the existing port-8742 daemon.
What only an install can show
First-run card. In fresh arm64 state the Dashboard displayed these exact
step details (01-dashboard-first-run.png):
| Step | Detail on this machine |
|---|---|
| Use an AI subscription you already pay for | “resmon found Claude Code and Codex on this machine.” |
| Or bring an API key | “No AI provider is configured.” |
| Add a key for a source that wants one | “5 of resmon’s sources can take a key of their own. The rest need none.” |
The footer explicitly distinguishes found/configured from working. Clicking
Skip, then reloading with Cmd-R, left the card absent
(first-run-skipped-reloaded.png).
A second new state/profile launch also opened Dashboard and displayed the card
(second-initial-dashboard.png). The published startup opens /index.html
without a route override, so the brief’s routine-before-first-Dashboard / card
never-appears sequence was not established. No startup patch or database seed
was substituted for that user path. Instead, without clicking Skip, the Routines
form created First routine retirement check, using the First of month preset.
The UI created it Active; it was immediately deactivated using the direct routine
control (second-first-routine.png). Returning to Dashboard removed the card,
and Cmd-R did not bring it back (second-dashboard-after-routine.png,
second-dashboard-reloaded.png). This establishes automatic retirement after
routine creation in that sequence; it does not prove the card never appeared.
Assistant. In the first isolated app the direct Routines form created
Release verification inactive routine with cron 0 0 1 * *. It initially
appeared Active and was deactivated before asking the assistant
(assistant-routine-inactive-before.png). AI, email and notification flags were
off. No scheduled execution was observed.
The read-only question “What sources do I have?” stayed at “Working…” and then
showed “resmon could not finish that turn.” after approximately five minutes
(assistant-readonly-working.png, assistant-readonly-failed.png). No answer,
tool-call entry, approval card, or cost line was displayed in the observed panel.
A process observation identified the real /Users/noir/.local/bin/claude child
of the bundled backend. This was not a mocked CLI. The panel did not report a
sign-in error, so this run does not establish that missing sign-in caused the
failure or that authentication succeeded.
The next request, “Please activate the routine named Release verification inactive
routine,” likewise ended after approximately five minutes with “resmon could
not finish that turn.” (assistant-activation-failed.png). No approval card
appeared in the observed panel, so there was nothing to deny. No assistant
card was approved. Reloading Routines showed the routine still Inactive
(assistant-routine-inactive-after.png). P3’s successful read/tool-call and
write-card/denial behavior remain unverified. The panel displayed no cost line
for either failed turn; cost is unavailable, not measured as zero. No application
repair or alternate runtime was introduced to obtain a different result.
Calendar and About App. 05-calendar.png shows left/right month chevrons,
not squares. 24-about-app.png shows “resmon version 2.0.1” and “Current release
line: 2.0.x.” These are published-window observations.
Console and visible discrepancies
After the route walk, the main renderer’s DevTools Console showed one warning:
Unrecognized feature: 'web-share'. (arm64-console.png). No font-blocking error
was present in that inspection. This was a post-walk snapshot, not continuous
capture of every renderer or webview; the Blog webview’s separate console was
not inspected. Startup stderr recorded [DEP0180] DeprecationWarning: fs.Stats
constructor is deprecated. Cloud Storage’s RefreshError, both assistant
failures, and the routine form’s initial Active status are observations, not
release grades or diagnoses. None was repaired by this task.
x64 / Rosetta result
file identified the copied executable as Mach-O 64-bit executable x86_64.
An existing com.apple.pkg.RosettaUpdateAuto receipt was present; no Rosetta
installation was performed. open -n --arch x86_64 launched the copied app with
fresh state/profile. Dashboard showed Backend Online and the first-run card
(x64-dashboard.png); About App showed 2.0.1 and 2.0.x
(x64-about-app.png). The initial native automation lookup timed out while the
app was starting; a later lookup of the same path succeeded. This was a bounded
Dashboard/About observation, not a full x64 route walk or an assistant check.
Isolation and cleanup
| Launch | App PID | Bundled backend PID | Backend port | Renderer port |
|---|---|---|---|---|
| arm64 first state | 63636 | 63642 | 55442 | 55452 |
| arm64 second state | 64376 | 64383 | 57256 | 57271 |
| x64 | 65684 | 65840 | 58328 | 58369 |
All six recorded app/backend PIDs were absent after quitting the task apps.
The two task-mounted DMGs were detached. Before/after lsof -nP -iTCP:8742
-sTCP:LISTEN snapshots were byte-for-byte identical: Python PID 1150, FD
12u, socket 0xaa26ce450f50da0e, 127.0.0.1:8742. No task launch bound
or attached to that port and no HTTP probe was made to it. This establishes
listener identity at two times, not unchanged HTTP health or continuous service.
Screenshots and receipts are retained outside the repository at
~/Documents/resmon-codex/release-evidence/v2.0.1-20260906/. Downloaded assets,
all three copied apps, and their temporary state/profile directories were removed
after attachment verification. The verification download was also removed.
Repository checks, distinct from the release walk
| Command | Observed result |
|---|---|
.venv/bin/python -m pytest -q |
1,546 passed, 2 skipped, 74 deselected, 106.50 s. |
npm run typecheck |
Exit 0. |
npm test |
275 passed, 32 suites passed, 7.616 s. |
npm run build |
Exit 0; renderer and Electron builds completed. |
npm run e2e |
69 passed, 2 skipped, 2.9 min on the macOS display. |
Backend tests ran from the checkout root; frontend commands from
resmon_scripts/frontend. The E2E packaged-app case skipped because the existing
local bundle is 1.8.4, not 2.0.1. Its no-CLI case skipped because it detected
the installed Codex command. It also printed P13c MONITOR NOT VERIFIED: a
no-HTTP execution finished before the Monitor poll adopted it. Those limitations
stay attached to the locally built E2E result; the published screenshots do not
replace them. The 74 live-network backend tests were not run.
Evidence and scope
The PR evidence comment attaches resmon-v2.0.1-launch-evidence.zip and four inline previews. The archive contains 37 screenshots: the numbered route screenshots and additional onboarding, assistant, console and x64 captures, a screenshot SHA-256 manifest, route denominator, rendered source list, release metadata, asset verification, and listener/process receipts. Screenshots were reviewed before publication and are not committed. A signed-in Chrome download of the attachment matched the original archive byte-for-byte; all four inline previews loaded. Unauthenticated command-line requests to the archive returned 404, so anonymous download access was not established.
The sole repository change is docs/release-verification.md. The procedure now
makes the two onboarding states, actual initial route, and assistant failure/cost
reporting explicit. No application code, dependency, workflow, or other document
changed. No release go/no-go judgment is made. Beyond the specific observations
above, this run did not establish quarantined Gatekeeper behavior, the exact
routine-before-first-Dashboard sequence, successful assistant answers/tool calls,
write approval/denial behavior, cost, video playback in the published app,
source searches, summaries, notifications, email, backup, updating, a full x64
walk, or Windows/Linux execution.
v2.0.2 run
Subject and launch conditions
Observed on 2026-09-07, macOS 26.3.1 (a), build 25D771280a, arm64
host. This is Delegation 12’s bounded recheck of the assistant on
the published v2.0.2 release,
published at 2026-09-07T05:30:39Z. Before verification, the clean Codex checkout
was fetched from upstream and branch codex/verify-v2.0.2 was created from
upstream/main, commit 4551b52d40d4fbb0f4dadcbc228263c0808b8306 (PR #109).
The checkout had been left on fix/assistant-silent-in-packaged-app, rather
than the main named in the brief; no uncommitted changes needed preservation.
All seven assets were downloaded with gh release download v2.0.2 --repo
ryanjosephkamp/resmon --dir /tmp/resmon-verify-v2.0.2-6qify9/assets. The arm64
DMG was mounted read-only and its app copied with ditto to that scratch tree,
outside both checkouts. The published app.asar was inspected to confirm the
state-directory override and daemon-lock lookup. LaunchServices used:
open -n -a /tmp/resmon-verify-v2.0.2-6qify9/arm64/resmon.app \
--env RESMON_STATE_DIR=/tmp/resmon-verify-v2.0.2-6qify9/arm64/state \
--stdout /tmp/resmon-verify-v2.0.2-6qify9/evidence/arm64-stdout.log \
--stderr /tmp/resmon-verify-v2.0.2-6qify9/evidence/arm64-stderr.log \
--args --user-data-dir=/tmp/resmon-verify-v2.0.2-6qify9/arm64/electron-user-data
State and Chromium profile were fresh. Neither RESMON_PYTHON nor
RESMON_E2E was supplied; the published app used its bundled Python. About App
displayed 2.0.2 (02-published-version.png). Native accessibility actions
operated the actual window. This was an isolated LaunchServices launch, not a
normal launch against the existing user’s database. OS keyring and CLI login
were not isolated or changed. The DMG and copied app root had provenance but no
quarantine attribute; no warning appeared. Quarantined Gatekeeper launch remains
unverified, and no attributes or security settings were changed.
Asset inventory against v2.0.1
All 7/7 names matched the v2.0.1 matrix after substituting the version. Downloaded byte sizes and computed SHA-256 digests matched v2.0.2 release metadata 7/7. Sizes below are bytes; the final column is the actual download.
| Asset | v2.0.1 bytes | v2.0.2 release bytes | Downloaded bytes |
|---|---|---|---|
latest-linux.yml |
378 | 378 | 378 |
latest.yml |
349 | 349 | 349 |
resmon-2.0.2-arm64.dmg |
221,270,814 | 221,298,582 | 221,298,582 |
resmon-2.0.2-setup-x64.exe |
194,928,665 | 194,935,371 | 194,935,371 |
resmon-2.0.2-setup-x64.exe.blockmap |
204,366 | 204,542 | 204,542 |
resmon-2.0.2-x64.dmg |
223,212,602 | 223,223,188 | 223,223,188 |
resmon-2.0.2-x86_64.AppImage |
250,853,419 | 250,866,052 | 250,866,052 |
The arm64 DMG SHA-256 was
f22567df8531501489e233b869e0a24a61bc98990ada378456d8b86fa6d56903.
All seven digests are in the attached asset receipt. Only arm64 was launched
for this recheck; asset agreement does not establish other platforms’ behavior.
Assistant outcome — P1 established for these two turns
The direct Routines form created Release verification v2.0.2 inactive routine
(routine 1), with arXiv, keyword release verification, and First of month
cron 0 0 1 * *. As in v2.0.1, the form initially created it Active. It was
immediately deactivated through the direct routine control before either
assistant request. AI, email and notification flags were off
(03-routine-inactive-before.png). No assistant card was approved.
Read. The exact question was what sources do I have?. The panel answered
through list_sources({}), with no approval card. It reported 27 sources,
identified CORE, GovInfo, NASA ADS and Springer Nature as requiring missing keys,
and distinguished PubMed and Semantic Scholar’s optional keys. The stored tool
result independently contains 27 source records and those key-status fields.
The question and completed-answer database timestamps were 07:10:22 and
07:10:31 UTC, nine seconds apart; this is second-resolution persisted timing,
not a measurement of the first streamed token. The displayed cost was
$0.1297, with cost_usd=0.1296855 in the stored message.
05-readonly-outcome.png, 06-readonly-tool-call.png and
07-readonly-answer-top.png show the answer, expanded empty call arguments and
cost. The full answer and returned data are preserved in the conversation
receipt. The answer’s phrase “usable without a key” describes catalog/key
information: no source search or upstream-health check was performed.
The answer’s Markdown table and emphasis markers appeared as literal text in
the panel; the Dashboard’s initial onboarding text still said 25 sources while
this tool returned 27. These are recorded observations, not repaired here.
Write and denial. The next request in the same conversation was
Please activate the routine named Release verification v2.0.2 inactive routine.
The assistant called list_routines({}), then requested
activate_routine({"routine_id":1}). A card appeared reading “Put routine 1
on its schedule”, showing those exact arguments, Allow, Deny, and
“Nothing runs until you answer. resmon is holding the assistant here.”
08-activation-card.png captures the full card before the decision.
Deny was clicked. The activation tool then showed a failure mark, and its
persisted result was is_error=true, The user did not allow this. The final
answer said “Not activated — the confirmation was declined.” and named the
routine as still inactive (09-activation-denied.png, 10-denial-answer.png).
The displayed cost was $0.1058, with a stored value approximately
0.1058115. These are the application’s reported costs, not independently
verified billing amounts.
The panel was closed and Routines reloaded with Cmd-R. The row remained
Inactive, with no last execution (11-routine-inactive-after-reload.png).
A read-only connection to this run’s isolated SQLite database confirmed
is_active=0 and zero executions. This establishes the requested card and
denial behavior for routine 1; it does not establish approval/execution behavior.
Both turns used the real /Users/noir/.local/bin/claude, with child PIDs
21105 and 21227 under bundled backend PID 20978. To avoid losing
diagnostics to the new 30-second startup deadline, a watcher captured ps eww,
lsof -nP -p <pid>, and a two-second sample about seven seconds into each
child’s lifetime. Both turns succeeded, so those samples are not evidence
of a blocked CLI or a diagnosis of v2.0.1. Raw diagnostics remain private;
only process/capture receipts are included in public evidence. No runtime,
authentication, environment or application repair was introduced to obtain
these outcomes.
Isolation and cleanup — P2
| App PID | Bundled backend PID | Backend port | Renderer port |
|---|---|---|---|
| 20972 | 20978 | 62191 | 62196 |
The task app was quit, all four recorded app/backend/CLI PIDs were absent,
and the task-mounted DMG was detached. Before/after
lsof -nP -iTCP:8742 -sTCP:LISTEN snapshots were byte-for-byte identical:
Python PID 1150, FD 12u, socket 0xaa26ce450f50da0e,
127.0.0.1:8742. No task launch bound or attached to 8742 and no HTTP probe
was made to it. This establishes unchanged listener identity at two times,
not HTTP health, database invariance or uninterrupted service.
The explicitly named delegation brief was read; no other file in
~/Documents/resmon-upgrade was accessed, and no file there was written.
The pre-existing installed application and daemon were left running.
Repository checks, distinct from published-app evidence
| Command | Observed result |
|---|---|
.venv/bin/python -m pytest -q |
1,549 passed, 2 skipped, 74 deselected, 150.04 s. |
npm run typecheck |
Exit 0. |
npm test |
275 passed, 32 suites passed, 10.561 s. |
npm run build |
Exit 0; renderer and Electron builds completed. |
RESMON_E2E_SCREENSHOT_DIR=/tmp/resmon-verify-v2.0.2-6qify9/e2e-screenshots npm run e2e |
71 passed, 2 skipped, 3.7 min on the macOS display. |
Backend tests ran at the checkout root and frontend commands in
resmon_scripts/frontend. E2E’s packaged walk skipped because the existing
local bundle is 1.8.4, not 2.0.2; its no-CLI case skipped because Codex is
installed. It also printed P13c MONITOR NOT VERIFIED because a no-HTTP
execution ended before the Monitor poll adopted it. The hotfix’s fake-CLI
first-event and silent-startup checks passed, the latter in 30.6 seconds.
Those local tests are separate from the real CLI/published DMG observations
above. The 74 live-network backend tests were not run.
Evidence and scope
The PR attachments contain resmon-v2.0.2-assistant-evidence.zip and inline
previews. The archive contains 11 native-window screenshots, their SHA-256
manifest, both release inventories, downloaded-asset verification, the complete
two-turn assistant record, the reloaded routine-state receipt, listener/process
receipts and test logs. Screenshots were reviewed before publication and are
not committed. Evidence is retained outside the repository at
~/Documents/resmon-codex/release-evidence/v2.0.2-20260907/.
The sole repository change is this document. Delegation 12’s explicit arm64 assistant recheck is the scope; this run does not repeat Delegation 11’s full route/onboarding/x64 walk or verify Windows/Linux, updating, source health, searches, summaries, notifications, email, backup or quarantined Gatekeeper launch. No application fix, dependency change or release judgment is included.
v2.2.0 run
Subject and launch conditions
Observed on 2026-09-20, macOS 26.3.1, arm64 host, by the maintainer’s lead agent.
The subject was the published v2.2.0 release,
published at 2026-09-20T20:03:28Z from tag v2.2.0 on 8f966d7 (PR #133). The
pre-tag gate (test_cumulative_upgrade.py, 16 cases) was green on the tag’s parent
before the release PR was cut, and the release PR itself merged with every required job
green on its exact head.
All seven assets were downloaded with gh release download v2.2.0 --repo
ryanjosephkamp/resmon into a scratch directory outside both checkouts. The arm64 DMG was
mounted read-only (hdiutil attach -readonly -nobrowse) and its app copied with ditto
into the scratch tree, never /Applications. The DMG carried com.apple.provenance and
no quarantine attribute; nothing was changed. The copied bundle’s Info.plist reported
CFBundleShortVersionString 2.2.0, CFBundleVersion 2.2.0, identifier
com.resmon.app. LaunchServices used the procedure’s isolated form:
open -n -a <scratch>/arm64/resmon.app \
--env RESMON_STATE_DIR=<scratch>/arm64/state \
--stdout <scratch>/evidence/arm64-stdout.log \
--stderr <scratch>/evidence/arm64-stderr.log \
--args --user-data-dir=<scratch>/arm64/electron-user-data
Neither RESMON_PYTHON nor RESMON_E2E was supplied; the published bundle used its own
Python. The startup log read [main] Starting backend on port 55952 then
[main] Backend is ready. lsof -nP -iTCP:8742 -sTCP:LISTEN showed the same pre-existing
listener before, during and after; port 8742 was never contacted.
Asset inventory against v2.1.0
All 7/7 names matched the v2.1.0 matrix after substituting the version. Downloaded
byte sizes matched the release metadata 7/7. The four large binaries grew by 1.3–1.5%
and the blockmap by 0.97%; that is consistent with the retained-PDF reader’s pinned PDF.js
assets and pypdf (PR #121), though the growth was not attributed by measurement. The two
updater feed files are byte-for-byte the same size.
| Asset | v2.1.0 bytes | v2.2.0 release bytes | Downloaded bytes |
|---|---|---|---|
latest-linux.yml |
378 | 378 | 378 |
latest.yml |
349 | 349 | 349 |
resmon-2.2.0-arm64.dmg |
221,393,745 | 224,785,384 | 224,785,384 |
resmon-2.2.0-setup-x64.exe |
195,002,354 | 197,500,855 | 197,500,855 |
resmon-2.2.0-setup-x64.exe.blockmap |
204,618 | 206,594 | 206,594 |
resmon-2.2.0-x64.dmg |
223,260,473 | 226,694,739 | 226,694,739 |
resmon-2.2.0-x86_64.AppImage |
250,943,385 | 254,495,346 | 254,495,346 |
The arm64 DMG SHA-256 was
166c28f129e4cf6dc8e88ba9d7943abff8bd06f434a2811be3c159c6533f197f.
The local API guard in the published build
This is the first time the token lock-down (PR #130, docs/local-api-security.md) ran
in a packaged app rather than a checkout. The isolated state directory held
api-token-55952 (mode 0600) beside resmon.port within seconds of launch. Against
the published backend on its own ephemeral port, from curl on the same host:
Request to GET /api/health |
Answer |
|---|---|
no Authorization header |
401 |
Authorization: Bearer not-the-token |
401 |
the token from api-token-55952 |
200; of the body’s six keys, status was ok and version was 2.2.0 |
right token, Origin: https://evil.example |
403 |
right token, Host: attacker.example |
403 |
Five of five answers are the ones the security document specifies. This establishes the guard on the shipped arm64 bundle at the HTTP layer only; it does not establish Windows token-file permissions, a browser drive-by page, or DNS rebinding (L-105 stands for those).
Isolation and cleanup
The five processes belonging to the copied app were sent SIGTERM. Five seconds later all
five were still present, so they were sent SIGKILL. The token file was therefore not
removed — that is the documented crash case (“a file left by a crash admits nothing”),
not the clean-shutdown case, and it was not retried with a longer wait. Whether the
packaged app exits cleanly on SIGTERM within a bounded time is recorded here as
unverified. The image was detached (disk4 ejected); the downloaded installers, the
copied app, its Chromium profile and its state directory were removed; the launch logs,
size and digest receipts, plist excerpt and the guard table above were kept outside the
repository.
What this run did not do
No route walk was performed in the published window, so this run does not claim that the 29 routes render; the built-app end-to-end job on the release head covers that surface for a locally built app, not the installer. The x64 DMG was not launched. Windows and Linux evidence is limited to asset names and byte sizes. No Gatekeeper prompt was exercised, because the command-line download carried no quarantine attribute. No assistant turn was attempted. The x64, Windows and Linux installers remain launch-unverified.
Evidence and scope
Kept outside the repository: release-metadata.txt, sizes.txt, sha256.txt,
dmg-xattr.txt, info-plist.txt, lsof-before.txt, arm64-stdout.log,
arm64-stderr.log, packaged-token-check.txt. This section is the only file this PR
changes.
v2.3.0 run
Subject and launch conditions
Observed on 2026-09-21, macOS 26.3.1, arm64 host, by the maintainer’s lead agent.
The subject was the published v2.3.0 release,
published at 2026-09-21T22:19:55Z from tag v2.3.0 on 2b2b7d0 (PR #156). The release
PR merged with every required job green on its exact head (c9e3a10), and the walk a
2.2.0 corpus takes (18 → 21) is proven in the ordinary suite over the v2.2.0 fixture
(test_one_init_db_over_the_newest_releases_fixture_keeps_every_row[v2.2.0]: 171 of 171
rows, 13 of 13 sequences).
All seven assets were downloaded with gh release download v2.3.0 into a scratch directory
outside both checkouts. The arm64 DMG was mounted read-only (hdiutil attach -readonly
-nobrowse) and its app copied with ditto into the scratch tree, never /Applications.
The DMG carried com.apple.provenance and no quarantine attribute; nothing was changed. The
copied bundle’s Info.plist reported CFBundleShortVersionString 2.3.0, CFBundleVersion
2.3.0, identifier com.resmon.app. LaunchServices used the isolated form of the
procedure above (open -n -a, RESMON_STATE_DIR and --user-data-dir under the scratch
tree, stdout and stderr captured). Neither RESMON_PYTHON nor RESMON_E2E was supplied;
the published bundle used its own Python. The startup log read [main] Starting backend on
port 52842 then [main] Backend is ready. lsof -nP -iTCP:8742 -sTCP:LISTEN showed the
same pre-existing listener before, during and after; port 8742 was never contacted.
Asset inventory against v2.2.0
All 7/7 names matched the v2.2.0 matrix after substituting the version. Downloaded byte
sizes matched the release metadata 7/7. Every asset is within ±0.25% of its v2.2.0
size: the release adds backend code and docs and removes tailwindcss, so the installers
neither grew nor shrank measurably. The two updater feed files are byte-for-byte the same
size.
| Asset | v2.2.0 bytes | v2.3.0 release bytes | Downloaded bytes |
|---|---|---|---|
latest-linux.yml |
378 | 378 | 378 |
latest.yml |
349 | 349 | 349 |
resmon-2.3.0-arm64.dmg |
224,785,384 | 224,727,061 | 224,727,061 |
resmon-2.3.0-setup-x64.exe |
197,500,855 | 197,510,191 | 197,510,191 |
resmon-2.3.0-setup-x64.exe.blockmap |
206,594 | 207,054 | 207,054 |
resmon-2.3.0-x64.dmg |
226,694,739 | 226,629,279 | 226,629,279 |
resmon-2.3.0-x86_64.AppImage |
254,495,346 | 254,396,713 | 254,396,713 |
The arm64 DMG SHA-256 was
555be6c8fe43f69db78568f37064bf0f83037918eeffcf6c27d30dc281f929b9; the x64 DMG
544a1ffe03bd553c299360a0958fe9e4c4f7172b83714460b7b2be6cb51591bb.
The published backend, its schema and its guard
The isolated state directory held api-token-52842 (mode 0600) beside resmon.port
within seconds of launch. Against the published backend on its own ephemeral port, from
curl on the same host:
Request to GET /api/health |
Answer |
|---|---|
no Authorization header |
401 |
Authorization: Bearer not-the-token |
401 |
the token from api-token-52842 |
200; status was ok, version was 2.3.0, identity.schema_version was 21 |
right token, Origin: https://evil.example |
403 |
right token, Host: attacker.example |
403 |
Five of five answers are the ones docs/local-api-security.md specifies. A fresh state
directory came up at schema 21; the 18 → 21 walk over an existing corpus is the suite’s
proof above, not this run’s.
Isolation and cleanup — and a clean exit this time
The five processes belonging to the copied app were sent SIGTERM. All five were gone
within one second, and the state directory afterwards held only resmon.db, -wal and
-shm: the token file had been removed, which is the documented clean-shutdown case. The
v2.2.0 run recorded this as unverified after a five-second wait and a SIGKILL; this run
waited up to thirty seconds and needed one. The packaged app’s exit on SIGTERM is now
verified on arm64 macOS, which closes the line the v2.2.0 section above left
unverified (it is not part of L-105, which is about the token not stopping the owner’s own
processes). The image was detached; the
downloaded installers, the copied app, its Chromium profile and its state directory were
removed; the launch logs, size and digest receipts, plist excerpt, health body and the guard
table above were kept outside the repository.
What this run did not do
No route walk was performed in the published window; the built-app end-to-end job on the release head covers that surface for a locally built app, not the installer. The x64 DMG was downloaded and hashed but not launched. Windows and Linux evidence is limited to asset names and byte sizes. No Gatekeeper prompt was exercised, because the command-line download carried no quarantine attribute. No assistant turn was attempted, no backup was taken and no restore was staged in the published app. The x64, Windows and Linux installers remain launch-unverified.
Evidence and scope
Kept outside the repository: release-metadata.json, assets.tsv, prev-assets.tsv,
sizes.txt, downloaded-vs-metadata.txt, sha256.txt, dmg-xattr.txt, info-plist.txt,
lsof-8742-before.txt, lsof-8742-during.txt, lsof-8742-after.txt, arm64-stdout.log,
arm64-stderr.log, packaged-token-check.txt, health-body.txt, processes.txt,
sigterm-exit.txt, state-after-stop.txt. This section is the only change in this PR.