Skip to content

appstore: messages carry a level, and a skipped dir is logged once - #48

Merged
TeoSlayer merged 2 commits into
mainfrom
fix/leveled-logs-and-quiet-skips
Oct 8, 2026
Merged

TeoSlayer merged 2 commits into
mainfrom
fix/leveled-logs-and-quiet-skips

Conversation

@TeoSlayer

Copy link
Copy Markdown
Contributor

Problem

On a Mac, in under an hour, the daemon's log held 1,574 copies of:

appstore skip io.pilot.wallet: read manifest: open /Users/…/.pilot/apps/io.pilot.wallet/manifest.json: no such file or directory
  • Where it comes from: the wallet binary, run outside the daemon, keeps its EVM identity in ~/.pilot/apps/io.pilot.wallet/identity-evm.json. That dir has no manifest. The supervisor rescans every 2 s and logged it every time.
  • How the Mac app shows it: it lists repeated warnings from the daemon's log. Every app-store line lands there, the routine starting: … installed_apps=0 included, because the app store logs through Go's log package and none of its lines has a level.

Fix

  • Skipped dirs are logged once. Each reason a scan skips a dir is logged once, and again only when the reason changes or the dir comes back. This uses the same scheme as the misnamed-dir lines from supervisor: adopt only <id>, not swap dirs; a fresh crash record per install #47, now applied to every skip reason: no manifest, a manifest that cannot be read, parsed or validated, the sideload policy, a binary path that escapes or is a symlink, and a misnamed dir.

  • A dir with no manifest.json is logged as "not an installed app; left alone", at INFO. Nothing in it is touched: for the wallet, it holds the private key.

  • Config.Slog. When set, it receives the messages with a level:

    • INFO for routine lines;
    • WARN for problems: crash loops and suspensions, failed verification, a manifest that does not parse or verify, refused downgrades, audit-log errors, and an app socket that never appears.

    Lines go out as appstore: <message>. Without Slog the output is unchanged. The daemon wires it in a follow-up hub PR once this is tagged.

Tests

  • TestScanInstalled_DirWithoutManifestIsLoggedOnce: 5 scans give 1 line, and the state file is untouched.
  • TestScanInstalled_SkipIsLoggedAgainOnlyWhenItsReasonChanges.
  • TestSlogCarriesTheLevel.

The first two fail when the log-once rule is disabled. go test -race ./... passes, and the package builds and vets for Linux too.

🤖 Generated with Claude Code

Seen on a Mac: the daemon's log held 1,574 copies of
"appstore skip io.pilot.wallet: read manifest: open .../manifest.json:
no such file or directory" in under an hour. The wallet binary, run
outside the daemon, keeps its EVM identity in
~/.pilot/apps/io.pilot.wallet, a dir with no manifest, and the rescan
(every 2 s) logged it every time. The Mac app counted these, and the
app store's routine "starting" line, as repeated warnings: the app
store logs through the log package, so none of its lines has a level.

- Every reason a scan skips a dir is logged once, and again only when
  the reason changes or the dir comes back. A dir with no manifest.json
  says it is not an installed app and is left alone (INFO).
- Config.Slog, when set, receives the messages with a level: INFO for
  routine ones, WARN for problems (crash loops and suspensions, failed
  verification, a manifest that does not parse or verify, refused
  downgrades, audit-log errors, a socket that never appears). Without
  it the output is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.03030% with 29 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
plugin/appstore/supervisor.go 70.12% 22 Missing and 1 partial ⚠️
plugin/appstore/logging.go 89.47% 4 Missing ⚠️
plugin/appstore/rlimit_linux.go 50.00% 1 Missing ⚠️
plugin/appstore/service.go 85.71% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Review of the leveled logs found warnings that would still repeat or that
routine operations raise, now that the daemon will show WARN:

- A refused downgrade stays on disk and was logged and audited on every
  rescan (about 1,800 times an hour). A rescan error and a resume marker
  that cannot be removed repeated the same way. These are logged once
  per condition (warnOnce), and again if it changes or comes back.
- Uninstall deletes the app's dir, and the supervise goroutine's last
  audit lines then failed with "audit open: no such file" as warnings.
  An audit write into a removed dir is now silent.
- pilotctl's install swaps a new dir in under the running app, taking its
  socket path with it: watchSocket warned "socket vanished", killed the
  app and the exit counted as a crash. When the install dir itself was
  replaced, it is now logged as a reinstall (INFO), the exit is not a
  crash, and the loop waits one rescan so an upgrade's own restart wins
  over re-verifying the new binary against the old pin.
- A manifest.json that is a dangling symlink is a read-manifest warning,
  not "not an installed app".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@TeoSlayer
TeoSlayer merged commit 19a118f into main Oct 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant