feat(0.23.0): gitignore-aware + non-destructive bootstrap (closes #67)#68
Conversation
…ootstrap (#67) Closes the safety gaps found dogfooding v0.22.0 on existing repos (stimulus, broomva.tech). Bootstrap was built for fresh workspaces; on repos with their own hooks/CI/gitignore it clobbered a tracked pre-commit and lacked gitignore awareness. Dep-Chain (P14): - upstream: install-l3-stability.sh pre-commit logic; bootstrap.sh Phase 2; arcs.v1 committable substrate; gh CLI (graceful-optional). - downstream: onboard.sh + repair.sh (call the same installer — inherit the fix); every workspace that runs `bstack bootstrap` on an existing repo. Fixed: - install-l3-stability.sh: never clobber a git-TRACKED .githooks/pre-commit (skip+warn; --force still moves aside). Untracked hooks still preserved as .local. Added: - bootstrap.sh Phase 2.6: gitignore reconciliation (auto-ignore .control/audit/*.jsonl; warn when committable substrate is ignored) + public-repo advisory (gh visibility). - tests/gitignore-aware-bootstrap.test.sh (5/5). Verified: new test 5/5; canary 14/14; shellcheck clean. VERSION 0.22.0→0.23.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR increments the version to 0.23.0 and addresses issue ChangesBootstrap hook and gitignore safety
🎯 2 (Simple) | ⏱️ ~12 minutes
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/bootstrap.sh`:
- Around line 168-171: The LOCAL_GLOBS array currently contains only
".control/audit/*.jsonl" and should also include ".claude/settings.json" so
user-specific Claude settings are treated as machine-local; update the
LOCAL_GLOBS definition (the LOCAL_GLOBS variable) to add the
".claude/settings.json" entry alongside ".control/audit/*.jsonl" so the
bootstrap flow ignores that file as intended.
- Around line 201-203: The gh check is running in the caller's cwd instead of
the intended workspace, so change the gh invocation to run from the workspace
directory (use the existing WORKSPACE_DIR/BROOMVA_WORKSPACE variable) — e.g. run
gh repo view inside a subshell that cds into "$WORKSPACE_DIR" (or use an
equivalent cd && gh pattern) so the gh repo view --json visibility --jq
.visibility call resolves the correct repository.
In `@scripts/install-l3-stability.sh`:
- Around line 110-113: The current move of "$PRE_COMMIT" to
"$WORKSPACE/.githooks/pre-commit.local" (inside the DRY_RUN check) will clobber
any existing .pre-commit.local; update the block that runs when DRY_RUN=0 to
detect an existing target and avoid overwrite — e.g., if
"$WORKSPACE/.githooks/pre-commit.local" exists, create a unique backup name
(append a timestamp or incrementing suffix) or fail with a clear message, then
mv "$PRE_COMMIT" to that unique path and chmod +x the new file; reference the
variables/commands PRE_COMMIT, WORKSPACE, .githooks/pre-commit.local, DRY_RUN,
mv and chmod when making the change.
- Around line 87-91: The current skip branch checks only for the "L3 rate gate"
marker and prevents overwriting even when the script was invoked with --force;
update the conditional that guards the skip so it respects the force flag (e.g.,
a parsed variable like FORCE or FORCE_FLAG). Concretely, modify the if in the
pre-commit check (the block referencing PRE_COMMIT, "L3 rate gate", SKIPPED) to
also require that the force flag is not set (for example: && [ "$FORCE" != "1" ]
or similar) before treating the hook as idempotent; this preserves the skip
behavior normally but allows --force to overwrite existing L3 hook content.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 34937def-891c-4fbc-883b-0b05add5e84f
📒 Files selected for processing (5)
CHANGELOG.mdVERSIONscripts/bootstrap.shscripts/install-l3-stability.shtests/gitignore-aware-bootstrap.test.sh
| # Machine-local globs that should always be ignored (absolute paths / runtime telemetry). | ||
| LOCAL_GLOBS=(".control/audit/*.jsonl") | ||
| # Committable substrate the loop needs — warn if a repo ignores these. | ||
| COMMITTABLE_FILES=(".control/arcs.yaml" ".control/rcs-parameters.toml" ".control/policy.yaml") |
There was a problem hiding this comment.
Also reconcile .claude/settings.json as machine-local.
Phase 2.6 only adds .control/audit/*.jsonl, but the machine-local set for this flow also includes .claude/settings.json. Without adding it here, bootstrap still leaves a user-specific settings file committable in a fresh repo.
Suggested fix
-LOCAL_GLOBS=(".control/audit/*.jsonl")
+LOCAL_GLOBS=(".claude/settings.json" ".control/audit/*.jsonl")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Machine-local globs that should always be ignored (absolute paths / runtime telemetry). | |
| LOCAL_GLOBS=(".control/audit/*.jsonl") | |
| # Committable substrate the loop needs — warn if a repo ignores these. | |
| COMMITTABLE_FILES=(".control/arcs.yaml" ".control/rcs-parameters.toml" ".control/policy.yaml") | |
| # Machine-local globs that should always be ignored (absolute paths / runtime telemetry). | |
| LOCAL_GLOBS=(".claude/settings.json" ".control/audit/*.jsonl") | |
| # Committable substrate the loop needs — warn if a repo ignores these. | |
| COMMITTABLE_FILES=(".control/arcs.yaml" ".control/rcs-parameters.toml" ".control/policy.yaml") |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/bootstrap.sh` around lines 168 - 171, The LOCAL_GLOBS array currently
contains only ".control/audit/*.jsonl" and should also include
".claude/settings.json" so user-specific Claude settings are treated as
machine-local; update the LOCAL_GLOBS definition (the LOCAL_GLOBS variable) to
add the ".claude/settings.json" entry alongside ".control/audit/*.jsonl" so the
bootstrap flow ignores that file as intended.
| if command -v gh >/dev/null 2>&1; then | ||
| _vis=$(gh repo view --json visibility --jq .visibility 2>/dev/null || true) | ||
| if [ "$_vis" = "PUBLIC" ]; then |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Locate and print the relevant section of scripts/bootstrap.sh (around lines 180-230)
if [ ! -f scripts/bootstrap.sh ]; then
echo "Missing scripts/bootstrap.sh"
exit 1
fi
echo "== scripts/bootstrap.sh (lines 160-240) =="
nl -ba scripts/bootstrap.sh | sed -n '160,240p'
# 2) Print where WORKSPACE_DIR is defined and how it's used (if present)
echo
echo "== WORKSPACE_DIR occurrences =="
rg -n "WORKSPACE_DIR" scripts/bootstrap.sh || true
# 3) Detect any prior cd into workspace (before the gh call)
echo
echo "== cd commands before the gh call section =="
# Print lines up to ~230 and look for cd
nl -ba scripts/bootstrap.sh | sed -n '1,240p' | rg -n "^[[:space:]]*[0-9]+[[:space:]]+cd[[:space:]]" || trueRepository: broomva/bstack
Length of output: 163
🌐 Web query:
gh repo view no repository argument targets current directory behavior
💡 Result:
When you run the command gh repo view without any arguments, the GitHub CLI automatically detects and displays the repository associated with your current working directory [1]. The command uses your local Git configuration to identify the remote repository corresponding to your current directory and retrieves its information—such as the owner, repository name, description, and README—from GitHub [1]. If the current directory is not part of a Git repository, the command will be unable to identify the target and will return an error [2]. Key details regarding this behavior: - Scope: It defaults to the repository linked to the current directory [1]. - Default Branch: By default, gh repo view displays information from the repository's default branch [3]. - No Arguments: Providing no arguments is the standard way to view the repository associated with your current project context, avoiding the need to manually type the owner/repo path [1].
Citations:
- 1: https://cli.github.com/manual/gh_repo_view
- 2: https://stackoverflow.com/questions/79622810/github-cli-says-current-directory-is-not-a-git-repository-even-after-running
- 3:
gh view repodoes not show current branch cli/cli#1971
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Show scripts/bootstrap.sh around lines 190-215 (avoid nl) =="
if [ ! -f scripts/bootstrap.sh ]; then
echo "Missing scripts/bootstrap.sh"
exit 1
fi
# Print with line numbers via awk
awk 'NR>=190 && NR<=215 { printf "%5d %s\n", NR, $0 }' scripts/bootstrap.sh
echo
echo "== Find gh repo view usages =="
rg -n "gh repo view" scripts/bootstrap.sh || true
echo
echo "== Find WORKSPACE_DIR definition/usage =="
rg -n "WORKSPACE_DIR" scripts/bootstrap.sh || true
echo
echo "== List cd commands before/near the gh repo view call =="
# Print first 250 lines with grep for "cd " lines
awk 'NR<=250 {print NR ":" $0}' scripts/bootstrap.sh | rg -n "^[0-9]+:.*\bcd\b" || trueRepository: broomva/bstack
Length of output: 2878
Resolve visibility from $WORKSPACE_DIR, not the caller’s cwd.
gh repo view with no repository argument uses the current working directory; since this script’s WORKSPACE_DIR can be overridden via BROOMVA_WORKSPACE but the advisory doesn’t cd into it (unlike other git -C "$WORKSPACE_DIR" calls), the PUBLIC/private advisory can come from the wrong repo. (cli.github.com)
Suggested fix
- _vis=$(gh repo view --json visibility --jq .visibility 2>/dev/null || true)
+ _vis=$(cd "$WORKSPACE_DIR" && gh repo view --json visibility --jq .visibility 2>/dev/null || true)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if command -v gh >/dev/null 2>&1; then | |
| _vis=$(gh repo view --json visibility --jq .visibility 2>/dev/null || true) | |
| if [ "$_vis" = "PUBLIC" ]; then | |
| if command -v gh >/dev/null 2>&1; then | |
| _vis=$(cd "$WORKSPACE_DIR" && gh repo view --json visibility --jq .visibility 2>/dev/null || true) | |
| if [ "$_vis" = "PUBLIC" ]; then |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/bootstrap.sh` around lines 201 - 203, The gh check is running in the
caller's cwd instead of the intended workspace, so change the gh invocation to
run from the workspace directory (use the existing
WORKSPACE_DIR/BROOMVA_WORKSPACE variable) — e.g. run gh repo view inside a
subshell that cds into "$WORKSPACE_DIR" (or use an equivalent cd && gh pattern)
so the gh repo view --json visibility --jq .visibility call resolves the correct
repository.
| if [ -f "$PRE_COMMIT" ] && grep -q "L3 rate gate" "$PRE_COMMIT" 2>/dev/null; then | ||
| # Already our hook — idempotent no-op (even under --force; reinstalling | ||
| # identical content is pointless). | ||
| echo " [skip] .githooks/pre-commit (already bstack L3 hook)" | ||
| SKIPPED=$((SKIPPED + 1)) |
There was a problem hiding this comment.
Honor --force when the existing file is already the L3 hook.
This branch skips solely on the marker string, so a workspace that already has the L3 hook can never receive template updates via --force. That breaks the script’s overwrite contract and leaves existing installs stuck on older hook content.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/install-l3-stability.sh` around lines 87 - 91, The current skip
branch checks only for the "L3 rate gate" marker and prevents overwriting even
when the script was invoked with --force; update the conditional that guards the
skip so it respects the force flag (e.g., a parsed variable like FORCE or
FORCE_FLAG). Concretely, modify the if in the pre-commit check (the block
referencing PRE_COMMIT, "L3 rate gate", SKIPPED) to also require that the force
flag is not set (for example: && [ "$FORCE" != "1" ] or similar) before treating
the hook as idempotent; this preserves the skip behavior normally but allows
--force to overwrite existing L3 hook content.
| echo " [info] existing .githooks/pre-commit found — preserving as .pre-commit.local" | ||
| if [ "$DRY_RUN" = "0" ]; then | ||
| mv "$PRE_COMMIT" "$WORKSPACE/.githooks/pre-commit.local" | ||
| chmod +x "$WORKSPACE/.githooks/pre-commit.local" |
There was a problem hiding this comment.
Don't overwrite an existing .pre-commit.local sidecar.
mv will clobber any existing .githooks/pre-commit.local, so a later forced install can destroy the previously preserved hook. Please guard this case or write to a unique backup name instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/install-l3-stability.sh` around lines 110 - 113, The current move of
"$PRE_COMMIT" to "$WORKSPACE/.githooks/pre-commit.local" (inside the DRY_RUN
check) will clobber any existing .pre-commit.local; update the block that runs
when DRY_RUN=0 to detect an existing target and avoid overwrite — e.g., if
"$WORKSPACE/.githooks/pre-commit.local" exists, create a unique backup name
(append a timestamp or incrementing suffix) or fail with a clear message, then
mv "$PRE_COMMIT" to that unique path and chmod +x the new file; reference the
variables/commands PRE_COMMIT, WORKSPACE, .githooks/pre-commit.local, DRY_RUN,
mv and chmod when making the change.
Closes #67.
Problem (found dogfooding v0.22.0 on real repos)
bstack bootstrapis built for fresh workspaces. On existing repos (stimulus, broomva.tech) it did two unsafe things + lacked one capability:.githooks/pre-commit— replaced broomva.tech's committed multimedia-asset validator with the bstack L3 hook, even thoughcore.hooksPath≠.githooks(so the bstack hook never fired). Destructive..control/.Fixes
install-l3-stability.sh— never clobber a tracked pre-commit.githooks/pre-commit+ no--force→ skip + warn (the committed hook is authoritative).--force→ preserves the current hook as.pre-commit.localfirst, then installs (the documented recovery path now actually exists in both the untracked and forced cases)..pre-commit.localas before.bootstrap.shPhase 2.6 — gitignore reconciliation + public-repo advisory.control/audit/*.jsonl) to.gitignore(idempotent).arcs.yaml,rcs-parameters.toml,policy.yaml) is gitignored — a coverage gap.gh repo view --json visibility(graceful if absent): surfaces that scaffolded governance will be public so secrets get reviewed before pushing.Verification (P11)
tests/gitignore-aware-bootstrap.test.sh— 7/7 (incl. the--force-preserves-sidecar + idempotency cases added after review).Cross-Review (P20)
Strata B fresh-context review: initial 7/10 PASS. It caught a real should-fix — the original
--forcecomment lied (it overwrote a tracked hook with no sidecar, covered by no test). Both fixed in this PR:--forcenow creates the sidecar, and two regression assertions (--force+ idempotency) were added.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests