Skip to content

feat(hub): explain a stale base, and show what this machine carries - #271

Merged
madarco merged 5 commits into
nightlyfrom
feat/system-page-diagnostics
Jul 28, 2026
Merged

feat(hub): explain a stale base, and show what this machine carries#271
madarco merged 5 commits into
nightlyfrom
feat/system-page-diagnostics

Conversation

@madarco

@madarco madarco commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Makes the System page answer the questions that cost an hour of manual archaeology when a Mac mini rebuilt the box image instead of pulling the published one.

Why a base is stale — now answerable

A stale provider used to say only build context changed (was abc…, now def…). It now opens to show which files changed:

E2B  stale — re-bake
  v why is this stale?
    ~ scripts/agentbox-open    d0c59ba → b54b0ff

computeContextSha256 already hashed every file and threw the digests away; computeContextManifest keeps them and the aggregate delegates to it, so stored fingerprints are unaffected by the refactor. The manifest is recorded at bake time by all six providers.

A base baked before manifests existed has nothing to diff against, and says so rather than guessing from mtimes — an mtime heuristic flags a touched-but-unchanged file and misses a changed one with a preserved mtime.

Carried from this machine — replaces the internal-file list

The old "Box image contents" section listed DOCKER_CONTEXT_FILE_MAP: ten files AgentBox itself bakes, which tells a user nothing about their setup. Replaced with what this machine hands to a box — agent configs, skills by name, ~/.gitconfig.

Derived from AGENT_SYNC_SPECS and the same pathExists gates the sync layer uses, so it is present-only: a path missing from that list is one no box will receive. Dot-dirs (.system, .cursor) are filtered — they are machinery, not skills.

Two things worth stating, since they contradict a reasonable assumption:

  • No user files are baked into the image. Skills reach a box at create time via volumes (docker) or staged tarballs (cloud). The only baked skill is our own agentbox-setup/SKILL.md.
  • The opencode sqlite db is deliberately not carried (excluded in registry.ts:169-171 and opencode.ts:138) because sessions live in a multi-tenant DB — so it is correctly absent from the list.

Box image card

The registry, the exact sha-… tag this host pulls, and the fingerprint it last stamped — precisely what had to be reconstructed by hand from docker-prepared.json + config + the registry.

Also fixes a real invalidation gap

Eight files Dockerfile.box COPYs were missing from DOCKER_CONTEXT_FILE_MAPgh-shim, git-shim, ntn-shim, linear-shim, chromium-resolver, agentbox-sshd-start, agentbox-portless-trust, opencode-agentbox-plugin.js. Editing any of them never invalidated the image, so a stale image shipped silently. The guard test now derives the expected set from the Dockerfile's COPY lines instead of spot-checking four names, which is why it never caught this.

Every stored fingerprint changes once as a result — expect a single re-bake or re-pull per machine. Confirmed live: docker went 54e4690b036d5b76d5109a39.

Verification

Against the standalone build, not next dev — the route still must not import @agentbox/sandbox-core, which is what 500'd the standalone build in #268. All new data flows through the __AGENTBOX_HUB_SYSTEM seam.

  • /api/v1/system200, zero Failed to load external module since process start.
  • Real agentbox prepare --provider docker recorded an 18-file manifest.
  • Edited one context file → the diff named exactly scripts/agentbox-open with before/after hashes; restored it → back to fresh.
  • The pre-existing e2b bake correctly shows the "no manifest recorded" path.
  • Browser-verified all three sections render, including the accordion.
  • typecheck 31/31, lint 17/17, tests 30/30 at concurrency 2.

https://claude.ai/code/session_01BcoSQCZtnaWV8rVJikyy5W


Note

Medium Risk
Touches box-image invalidation and prepared-state shape across all providers; one-time fingerprint churn and re-bake/re-pull is expected, but the fix prevents silent stale images after shim/script edits.

Overview
The hub System page and GET /api/v1/system now explain why a provider base is stale and what the host actually gives boxes, instead of only listing AgentBox’s own baked files.

Stale bases: Bake flows record a per-file manifest (computeContextManifest / diffFileManifests) and expose bakeDiff on stale providers. The UI shows changed/added/removed paths (or “no manifest — re-bake to diff”). All six providers persist files at prepare time and implement currentBaseFileHashes on the provider module.

System page content: imageContextKeys is replaced by hostCarried (present-only agent configs, skills by name, ~/.gitconfig via collectHostCarried + AGENT_SYNC_SPECS) and a boxImage card (registry, sha-… pull tag, stamped fingerprint). Data is wired through __AGENTBOX_HUB_SYSTEM in server.ts, not from Next routes importing sandbox-core.

Fingerprint fix: Eight Dockerfile.box COPY’d scripts were added to DOCKER_CONTEXT_FILE_MAP; a test now requires every COPY line to appear in the map. Stored fingerprints change once until each machine re-bakes or re-pulls.

Reviewed by Cursor Bugbot for commit 4bdaa5b. Configure here.

madarco added 2 commits July 28, 2026 11:22
The aggregate contextSha256 can only say the build context changed, never which
file changed - the one thing anyone wants when a provider reports stale.
computeContextSha256 already hashed every file and threw the digests away;
computeContextManifest keeps them and the aggregate delegates to it, so stored
fingerprints are unaffected.

Also fixes a real invalidation gap: eight files Dockerfile.box COPYs were missing
from DOCKER_CONTEXT_FILE_MAP (gh-shim, git-shim, ntn-shim, linear-shim,
chromium-resolver, agentbox-sshd-start, agentbox-portless-trust,
opencode-agentbox-plugin.js), so editing any of them never invalidated the image.
The guard test now derives the expected set from the Dockerfile's COPY lines
instead of spot-checking four keys, which is why it never caught this. Every
stored fingerprint changes once as a result.

Claude-Session: https://claude.ai/code/session_01BcoSQCZtnaWV8rVJikyy5W
The System page listed DOCKER_CONTEXT_FILE_MAP - agentbox's own baked assets -
which tells a user nothing about their setup, and a stale provider said only that
the fingerprint moved. Both gaps cost an hour of manual archaeology when a Mac
mini rebuilt the box image instead of pulling the published one.

- stale rows open to show WHICH files changed, diffed against the per-file
  manifest now recorded at bake time; a base baked before manifests existed says
  so instead of guessing from mtimes
- 'Carried from this machine' replaces the internal-file list: agent configs,
  skills by name, ~/.gitconfig - present-only, derived from AGENT_SYNC_SPECS and
  the same pathExists gates the sync layer uses, so an absent path is one no box
  receives
- a 'Box image' card with the registry, the exact sha- tag this host pulls and
  the fingerprint it last stamped

All of it flows through the __AGENTBOX_HUB_SYSTEM globalThis seam - the route
still must not import @agentbox/sandbox-core, which is what 500'd the standalone
build in #268. Verified against build:standalone, not next dev.

Claude-Session: https://claude.ai/code/session_01BcoSQCZtnaWV8rVJikyy5W
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agentbox-web Skipped Skipped Jul 28, 2026 10:57am

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4bdaa5b. Configure here.

Comment thread apps/hub/lib/hub-backend.ts Outdated
if (!stored || Object.keys(stored).length === 0) return { hasManifest: false };
const mod = (await IMPORTERS[id]()).providerModule;
const current = await mod.currentBaseFileHashes?.();
if (!current) return { hasManifest: false };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale diff hides stored manifest

Medium Severity

When a bake already has a per-file manifest but currentBaseFileHashes cannot run, providerBakeDiff returns hasManifest: false. The stale accordion then says no manifest was recorded, which is wrong and sends users toward a needless re-bake instead of a fingerprint or runtime resolution issue.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4bdaa5b. Configure here.

kind: isSkillHost ? 'skills' : 'config',
...(isSkillHost ? { skills: childDirs(skillsDir) } : {}),
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenCode state path false positive

Low Severity

collectHostCarried lists every existing OpenCode staticPaths entry, including ~/.local/state/opencode, but docker create only enables OpenCode when ~/.config/opencode or ~/.local/share/opencode exists (or opencodeConfig is set). A host with only the state directory can show a carried path that create never syncs.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4bdaa5b. Configure here.

System diagnoses a stale base but has no bake control - that lives on Settings -
so a user landing on a re-bake nag had to guess where to go. The Providers header
now links to /settings#providers ('Bake in Settings' when something is stale,
'Manage in Settings' otherwise), and the providers card carries the anchor.

Sidebar order follows: System sits under Settings, since it is the read-only
companion to it rather than a peer of Custody.

Claude-Session: https://claude.ai/code/session_01BcoSQCZtnaWV8rVJikyy5W
The System page's link to Settings used a bespoke style with state-dependent
wording; use the shared Button (outline/sm) like every other page, labelled
just 'Settings'.

Settings auto-expanded the credential form for every provider without a key, so
a fresh hub opened as a wall of forms. The badge already says which providers
need attention - start collapsed and let the user open the one they came for.

Claude-Session: https://claude.ai/code/session_01BcoSQCZtnaWV8rVJikyy5W
…ode path

Two Bugbot findings, both confirmed against the source:

- providerBakeDiff returned hasManifest:false when currentBaseFileHashes could
  not run, even though a manifest WAS recorded. The accordion then told the user
  to re-bake to enable a diff they already had, when the real problem is
  resolving the current assets. New liveUnavailable flag renders that honestly.
- collectHostCarried listed ~/.local/state/opencode on a host that has only that
  dir, but create.ts's wantOpencode gates on ~/.config/opencode or
  ~/.local/share/opencode - the state dir rides along and never enables the
  volume itself. Listing it alone promised a box a path create never mounts,
  breaking the present-means-carried contract.

Claude-Session: https://claude.ai/code/session_01BcoSQCZtnaWV8rVJikyy5W
@madarco
madarco merged commit 88333a5 into nightly Jul 28, 2026
4 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant