ensure_seeded() mirrors the packaged src-tauri/data/ tree into ~/.base-studio-code/config/ on first boot, seed_files writes only where absent, and load_opt prefers the on-disk copy. So the config dir is written once, never updated, and always wins at read time.
Every install is therefore permanently pinned to the prompts, protocols, permissions and shell hooks it shipped with. No improvement to any of them ever reaches an existing user.
Measured, not theorised
Audited this machine's mirror against the current seed: 192 files, 44 differing, 26 retired seeds still overlaying. Not customisation — staleness. Among the 44:
| File |
Why it matters |
permissions/base.json, permissions/role-capabilities.json |
the security allow-list — the posture running here is not the one we ship |
shell/confine.sh, coord-emit.sh, activity.sh, fleet.sh |
the PreToolUse hooks + bsc-* emitters — a security-hook fix silently does not apply (already seen once in #3530) |
fleet/worker-protocol.md, director-protocol.md |
including #4191's "read from the stores, not copies" precedence rule |
planner/process.md, intro.*, stage-registry.json |
the planner's core spec |
all 6 blueprints/*.json, 5 personas/*.json, 4 stages/*.json |
|
stages/features.json here had no requires field at all — the entire algorithm-library / reuse-first instruction (#4191/#3892) had never reached the planner running on this machine.
And because the frontend overlay is additive by stem, the 26 retired seeds still apply: the deleted complete / feature-add / serverless-function / ui-kit / blueprint-author blueprints were still being offered.
The codebase already diagnosed this — for one directory
SEED_SKIP_DIRS = ["components"] exists verbatim because "an auto-seeded copy became a de-facto override that pinned every install's effective seed at its first-run roster — refresh/retire verdicts could never fire (#2514)". components/ was excluded from seeding and given a hash-based refresh (#2483). Every other tree still has the bug.
The fix — hash-based refresh, mirroring #2483
Record the hash of what was seeded, per file, in a manifest outside the overlaid tree (a file inside config/ would itself be served by get_config_files). Then on boot, per seed file:
- absent → write it, record the hash (today's behaviour)
- present, hash matches what we seeded → the user never touched it: overwrite with the new seed, update the hash
- present, hash differs → a genuine edit: leave it, and surface that it has diverged so it is visible rather than silent
- seed gone, copy unmodified → remove it, which is what stops retired blueprints resurrecting
- seed gone, copy modified → leave it, surface it as orphaned
Chosen over the alternative (stop seeding these trees, as #2514 did for components/) because the overlay exists so config can be customised, and silently dropping that is a behaviour change for anyone who has. Hash-based keeps the capability and makes staleness impossible.
Acceptance
- A manifest of seeded hashes, stored outside
config/.
- The five cases above, unit-tested against a temp home — in particular that a modified file is never clobbered and an unmodified one always refreshes.
- Retired seeds are removed when unmodified.
- Divergences are surfaced, not silent.
- Migration for existing installs: no manifest yet ⇒ treat every file as unknown. Do not clobber — hash it against the shipped seed and, where it matches, adopt it into the manifest; where it does not, leave it and report it as diverged (it may be stale rather than edited, and we cannot tell them apart without history).
- Gate:
cargo check / clippy --all-targets / cargo test.
Note
This machine's mirror was backed up and cleared by hand while diagnosing (~/.base-studio-code/config.bak-20260802T030849Z), so it is currently clean — which means it is not a test case for the migration path. Test that against a synthesised stale home.
Related: #2483 · #2514 · #2027 · #3530.
ensure_seeded()mirrors the packagedsrc-tauri/data/tree into~/.base-studio-code/config/on first boot,seed_fileswrites only where absent, andload_optprefers the on-disk copy. So the config dir is written once, never updated, and always wins at read time.Every install is therefore permanently pinned to the prompts, protocols, permissions and shell hooks it shipped with. No improvement to any of them ever reaches an existing user.
Measured, not theorised
Audited this machine's mirror against the current seed: 192 files, 44 differing, 26 retired seeds still overlaying. Not customisation — staleness. Among the 44:
permissions/base.json,permissions/role-capabilities.jsonshell/confine.sh,coord-emit.sh,activity.sh,fleet.shbsc-*emitters — a security-hook fix silently does not apply (already seen once in #3530)fleet/worker-protocol.md,director-protocol.mdplanner/process.md,intro.*,stage-registry.jsonblueprints/*.json, 5personas/*.json, 4stages/*.jsonstages/features.jsonhere had norequiresfield at all — the entire algorithm-library / reuse-first instruction (#4191/#3892) had never reached the planner running on this machine.And because the frontend overlay is additive by stem, the 26 retired seeds still apply: the deleted
complete/feature-add/serverless-function/ui-kit/blueprint-authorblueprints were still being offered.The codebase already diagnosed this — for one directory
SEED_SKIP_DIRS = ["components"]exists verbatim because "an auto-seeded copy became a de-facto override that pinned every install's effective seed at its first-run roster — refresh/retire verdicts could never fire (#2514)".components/was excluded from seeding and given a hash-based refresh (#2483). Every other tree still has the bug.The fix — hash-based refresh, mirroring #2483
Record the hash of what was seeded, per file, in a manifest outside the overlaid tree (a file inside
config/would itself be served byget_config_files). Then on boot, per seed file:Chosen over the alternative (stop seeding these trees, as #2514 did for
components/) because the overlay exists so config can be customised, and silently dropping that is a behaviour change for anyone who has. Hash-based keeps the capability and makes staleness impossible.Acceptance
config/.cargo check/clippy --all-targets/cargo test.Note
This machine's mirror was backed up and cleared by hand while diagnosing (
~/.base-studio-code/config.bak-20260802T030849Z), so it is currently clean — which means it is not a test case for the migration path. Test that against a synthesised stale home.Related: #2483 · #2514 · #2027 · #3530.