fix(bin): resolve the startup-memory budget through a symlinked config directory - #5
Closed
doitdigital0495 wants to merge 2 commits into
Closed
fix(bin): resolve the startup-memory budget through a symlinked config directory#5doitdigital0495 wants to merge 2 commits into
doitdigital0495 wants to merge 2 commits into
Conversation
A home whose config/ is a symlink into a machine-configuration repo is a supported deployment layout, and every other setting under config/ is already read through it. Only the startup-memory budget refused, so fm-startup-memory-budget.sh read/report failed outright and every session start printed "STARTUP_MEMORY_BUDGET: invalid config/startup-memory-budget - config directory is symlinked". The rejection bought no integrity property. It tested only the final path component, so a symlinked ancestor passed silently, and it could never detect the thing it looked like it was guarding: two homes resolving to one physical config directory. The checks that actually stop this scalar from being aliased or shared are per-file and are unchanged - the budget file itself must still be a regular, non-symlinked, single-linked file holding exactly one positive decimal value and one newline. The directory now only has to resolve to a real directory, so a dangling symlink and a symlink to a file are still rejected, with the existing "config directory is not a directory" diagnostic. Also anchors the config ignore rule to /config so a symlinked config/ is ignored rather than showing up as an untracked entry in a captain's home.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Take the already-written, captain-reviewed branch fm/fm-config-symlinked through the no-mistakes pipeline so it lands as a PR with every CI check green, including this repo's own 'PR must be raised via no-mistakes' body-signature gate (the only failing check on the existing PR #5, which was pushed directly rather than through the gate).
Goal of the change itself: bin/fm-startup-memory-budget-lib.sh must resolve the startup-memory budget through a symlinked config/ directory. Every other setting under config/ already reads fine through such a symlink - a firstmate home whose config/ is linked into a machine-configuration repo is a supported deployment layout - but the startup-memory budget alone refused with 'config directory is symlinked'. That made every /stow incomplete and printed 'STARTUP_MEMORY_BUDGET: invalid' at every session start.
Deliberate decisions already made and approved by the captain, which should not be reverted or re-litigated:
Constraints: this is firstmate's own repo, so the firstmate-coding-guidelines skill applies - one sentence per line in tracked Markdown, plain dashes only, no agent co-author on commits, bin/*.sh must pass bin/fm-lint.sh (pinned shellcheck), tests colocated in tests/ as .test.sh extending the existing script, and tests must exercise behavior through the executable interface rather than asserting implementation source bytes. Scope is deliberately limited to these four files; do not expand it. Do not merge the PR - the captain approves the merge.
What Changed
bin/fm-startup-memory-budget-lib.shno longer rejects aconfig/directory that is itself a symlink;fm_startup_memory_budget_config_dir_safenow only requires the path to resolve to a real directory, while the existing per-file regular/single-linked/non-symlinked checks are unchanged.tests/fm-startup-memory-budget.test.shcovers bootstrap materialization andreadthrough a symlinkedconfig/, plus rejection when the budget file behind the symlink is itself a symlink, and when theconfig/symlink points at a missing path or at a regular file.docs/configuration.mddocuments the symlinked-config/layout as supported, and.gitignoreanchors the rule at the repo root by changingconfig/to/config.Risk Assessment
✅ Low: Small, well-bounded change that removes a check with no integrity value at the single shared boundary all consumers route through, keeps every per-file safety guard, and adds executable-interface regression coverage for both the read and bootstrap-materialize paths.
Testing
Exercised the startup-memory budget through a symlinked config/ directory end-to-end: the colocated suite passes at HEAD and its new symlinked-config assertion fails against the pre-fix library, and CLI transcripts show session start going from "STARTUP_MEMORY_BUDGET: invalid config/startup-memory-budget - config directory is symlinked" with nothing materialized, to a silent, complete start that materializes and reads the budget through the symlink while still rejecting a symlinked budget file, a dangling config/ symlink, and a config/ symlink to a regular file. The .gitignore anchoring was verified through git check-ignore rather than by reading the file, and tests/fm-gitignore-config.test.sh passes; tests/fm-bootstrap.test.sh has one pre-existing environment failure identical at the base commit. No UI surface is involved - this is a shell CLI and session-start path, so evidence is CLI transcripts rather than screenshots.
Evidence: Session start through a symlinked config/ - before vs after
Source: Session start through a symlinked config/ - before vs after
################ BEFORE fix (base bc57f60) ################ === session start (bin/fm-bootstrap.sh) === STARTUP_MEMORY_BUDGET: invalid config/startup-memory-budget - config directory is symlinked $ cat $FM_HOME/config/startup-memory-budget cat: .../firstmate-config/startup-memory-budget: No such file or directory ################ AFTER fix (c362928) ################ === session start (bin/fm-bootstrap.sh) === (no output - clean, complete session start) $ cat $FM_HOME/config/startup-memory-budget 7500 === integrity guarantees still enforced through the symlink === budget file itself a symlink: startup-memory-budget: invalid config/startup-memory-budget - file is symlinked (exit=1) config/ symlink -> missing path: startup-memory-budget: invalid config/startup-memory-budget - config directory is not a directory (exit=1) config/ symlink -> regular file: startup-memory-budget: invalid config/startup-memory-budget - config directory is not a directory (exit=1)Evidence: bin/fm-startup-memory-budget.sh read/report on a symlinked config - before vs after
Source: bin/fm-startup-memory-budget.sh read/report on a symlinked config - before vs after
$ ls -l ~/.firstmate/config config -> /tmp/fm-demo/machine-config/firstmate-config $ cat .../startup-memory-budget -> 9000 === BEFORE (bc57f60) === $ bin/fm-startup-memory-budget.sh read startup-memory-budget: invalid config/startup-memory-budget - config directory is symlinked exit=1 $ bin/fm-startup-memory-budget.sh report startup-memory-budget: invalid config/startup-memory-budget - config directory is symlinked exit=2 === AFTER (c362928) === $ bin/fm-startup-memory-budget.sh read 9000 exit=0 $ bin/fm-startup-memory-budget.sh report estimator=ceil(UTF-8 bytes / 3) conservative-local-estimate role=primary effective_budget_tokens=9000 file=data/captain.md bytes=14 estimated_tokens=5 status=present total_estimated_tokens=5 budget_status=within-budget exit=0Evidence: .gitignore anchoring checked through git check-ignore
Source: .gitignore anchoring checked through git check-ignore
--- .gitignore @ bc57f60 (... config/) config/startup-memory-budget IGNORED bin/backends/config/settings IGNORED --- .gitignore @ HEAD (... /config) config/startup-memory-budget IGNORED bin/backends/config/settings trackedPipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
tests/fm-startup-memory-budget.test.sh:172- The added coverage only exercisesreadthrough a symlinkedconfig/, but the reported symptom (STARTUP_MEMORY_BUDGET: invalid ...at every session start, incomplete/stow) is produced byfm_startup_memory_budget_materializevia bin/fm-bootstrap.sh:1095, which routes through the samefm_startup_memory_budget_config_dir_safegate and additionally doesmktemp/lninside the symlinked directory. Nothing fails before the fix and passes after it for that path. Add one case (e.g. intest_primary_bootstrap_materializes_visible_default) that runs the bootstrap against a home whoseconfig/is a symlink to an empty real directory and asserts the 7500 default is published into the symlink target.🔧 Fix: test bootstrap materialization through symlinked config dir
✅ Re-checked - no issues remain.
tests/fm-bootstrap.test.sh- tests/fm-bootstrap.test.sh fails identically at the base commit bc57f60 and at the target commit (not ok - the unsplit run lost its local diagnostic, outputNEEDS_GH_AUTH). Pre-existing and environment-driven (no gh auth in this sandbox), not caused by this change. Note: this suite's git fixtures also pick up the machine's global git template hooks, soGIT_TEMPLATE_DIRmust be neutralized to run them locally.bash tests/fm-startup-memory-budget.test.sh(with an emptyGIT_TEMPLATE_DIRso the machine's global git hooks do not block fixture commits) - all 4 cases passRegression proof: replacedbin/fm-startup-memory-budget-lib.shwith itsbc57f60version and reran the same suite - fails withnot ok - bootstrap rejected a symlinked config directory: STARTUP_MEMORY_BUDGET: invalid config/startup-memory-budget - config directory is symlinked; restored afterwards (worktree verified clean)Manual CLI check on a home withconfig/ -> machine-config/firstmate-config:FM_HOME=... bin/fm-startup-memory-budget.sh readandreport, before (exit 1 / exit 2, "config directory is symlinked") vs after (9000, full report, exit 0)Manual session-start check:FM_BACKEND=tmux FM_HOME=<symlinked-config home> FM_ROOT_OVERRIDE=<fixture root> bin/fm-bootstrap.shwith a faked toolchain - before: printsSTARTUP_MEMORY_BUDGET: invalid ...and materializes no budget file; after: silent, materializes7500through the symlinkManual integrity checks through the symlink: budget file itself a symlink ->file is symlinked; config/ symlink to a missing path and to a regular file ->config directory is not a directory(all exit 1)git check-ignorein throwaway repos using the base and HEAD.gitignore:config/startup-memory-budgetignored in both,bin/backends/config/settingsignored at base but tracked at HEADbash tests/fm-gitignore-config.test.sh- passesbash tests/fm-bootstrap.test.shat both base and target - identical pre-existing failure (NEEDS_GH_AUTH), unrelated to this change✅ **Document** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.