Skip to content

fix(deploy): gate pi-compaction on pi like the other ~/.pi/agent entries - #2334

Merged
mlorentedev merged 3 commits into
mainfrom
fix/pi-compaction-requires
Oct 11, 2026
Merged

mlorentedev merged 3 commits into
mainfrom
fix/pi-compaction-requires

Conversation

@mlorentedev

Copy link
Copy Markdown
Owner

Problem

pi-compaction was the one ~/.pi/agent entry in ai/deploy.json without "requires": "pi" (added in #1938; pi, pi-mcp and pi-nan-provider all have it). On a box without pi, the deploy created ~/.pi/agent/settings.json holding only the compaction keys, and dotf doctor then failed its pin: pin "pi-deployed-default-model": key "defaultModel" not present. Found by the from-zero job on macOS (#2013 X1, #2331). A box with pi is unaffected: the merge lands next to the keys pi already wrote.

Change

  • ai/deploy.json: pi-compaction declares "requires": "pi".
  • cli/internal/deploy/manifest_requires_test.go: reads the shipped manifest and fails for an entry with no requires when every other entry in its destination directory declares the same one. A directory mixing tools ({HOME}: tmux, zsh) imposes nothing. zsh-functions-sh is exempt by name with its reason (bash sources it too).

Evidence (macOS arm64)

$ go test ./internal/deploy/ -run Requires        # before the deploy.json change
manifest_requires_test.go:34: pi-compaction omits the requires every other entry in its directory declares; ...
FAIL
$ go test ./internal/deploy/                      # after
ok  	github.com/mlorentedev/dotfiles/cli/internal/deploy	0.148s
$ go build ./... && go vet ./... && GOOS=windows go vet ./... && go test ./...
ok (every package; internal/review has no test files)
$ golangci-lint run ./...
0 issues.
$ bats antigravity claude-settings-template copilot-config opencode pi-config profile-heal-ps1 setup-linux setup-windows
1..345, 345 ok

Mutation: the zsh-functions-sh exemption removed -> the guard FAILs naming zsh-functions-sh, so the exemption is load-bearing. tests/verify-setup.bats also references deploy.json; it runs in the integration job.

Knowledge

SDD skip rationale

One manifest line plus its guard test; no production Go changes.

On a box without pi the entry created a settings.json holding only the
compaction keys, and doctor's defaultModel pin failed against it. A guard
now requires an entry to declare the requires every sibling in its
directory declares, with zsh-functions-sh exempt because bash sources it.
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 49 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: mlorentedev/dotfiles/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 589d1209-35d6-4cae-8494-813681a8c680

📥 Commits

Reviewing files that changed from the base of the PR and between f244079 and 8191870.


📒 Files selected for processing (2)
  • ai/deploy.json
  • cli/internal/deploy/manifest_requires_test.go

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment on lines +23 to +37
func TestManifest_AnEntryRequiresWhatEverySiblingInItsDirectoryRequires(t *testing.T) {
raw, err := os.ReadFile(filepath.Join("../../..", ManifestRel))
if err != nil {
t.Fatal(err)
}
m, err := ParseManifest(raw)
if err != nil {
t.Fatal(err)
}
for _, name := range missingSiblingRequires(m.Configs) {
if _, ok := requiresExempt[name]; !ok {
t.Errorf("%s omits the requires every other entry in its directory declares; add it, or exempt it in requiresExempt with the reason", name)
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Vacuous guard

THEORETICAL. TestManifest_AnEntryRequiresWhatEverySiblingInItsDirectoryRequires iterates missingSiblingRequires(m.Configs) and only errors for names not in requiresExempt. Once this PR makes the manifest clean, that slice is empty and the loop body never executes, so the test makes no positive assertion about the shipped manifest. If ParseManifest ever yielded an empty Configs (field rename, parse regression, manifest renamed) the test would still pass green and the regression it guards would go unnoticed. The helper's logic is covered by TestMissingSiblingRequires, so only the manifest read is unguarded; adding an assertion that the parsed manifest is non-empty (or that pi-compaction is present with Requires == "pi") closes the gap.

Comment on lines +100 to +102
if len(got) != len(tc.want) || (len(got) == 1 && got[0] != tc.want[0]) {
t.Errorf("%s: got %v, want %v", tc.name, got, tc.want)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Weak assertion

THEORETICAL. The comparison len(got) != len(tc.want) || (len(got) == 1 && got[0] != tc.want[0]) checks element names only when exactly one entry is missing. A future table case with two or more missing entries is compared by length alone, so wrong names or duplicates would still pass. No current case triggers it, but the table is clearly meant to grow; an element-wise comparison (or sorting and reflect.DeepEqual) removes the trap.

@github-actions

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis ❌

2013 - Partially compliant

Compliant requirements:

  • Fix landed together with a guard (the new manifest test).
  • Small single-purpose diff, well under ~300 LOC.
  • No setup-macos.sh fork introduced.
  • No auto-merge enabled.

Non-compliant requirements:

  • Does not implement any W1–W10 row.
  • Does not implement any P-track row.
  • No spec folder or skip-sdd rationale tied to a track of this epic.

Requires further human verification:

  • Whether this bug fix maps to a ledger finding / row of the epic and therefore should reference the corresponding wave, or is incidental cleanup.

843 - Not compliant

Non-compliant requirements:

1312 - Not compliant

Non-compliant requirements:

  • No setup-linux.sh reordering.
  • No ordering bats test.
  • No loud npm-absent error.
⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 PR contains tests
🔒 No security concerns identified

⚙️ Agent run details
  • Model: openai/deepseek-v4-flash
  • Tokens: 18,969 in / 8,337 out / 27,306 total
  • Time cost: 66.0s
  • AI calls: 1

…ct comparisons

On a clean manifest the guard's loop never ran, so an empty read would have
passed. It now asserts pi-compaction is declared with requires pi and that
every exemption names a live entry. The helper sorts its result and the
table compares it with slices.Equal, with a two-miss case.
@mlorentedev

Copy link
Copy Markdown
Owner Author

Review triage

Reviewed by pr-agent (github-actions, COMMENTED review with two inline comments on 71df086). CodeRabbit did not review.

# Finding Disposition
1 Vacuous guard (manifest_requires_test.go:37): on a clean manifest the loop never runs, so an empty or renamed read passes Applied in 8191870: asserts pi-compaction is declared with requires: pi, and that every requiresExempt name is still in the manifest (a stale exemption fails). Mutations: requires removed from pi-compaction -> FAIL (both asserts); an exemption naming a missing entry -> FAIL
2 Weak assertion (:102): names compared only when exactly one entry is missing Applied in 8191870: missingSiblingRequires sorts its result (it came from map iteration), the table uses slices.Equal, and a new case has one miss in each of two directories. Mutation: sort removed -> that case FAILs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant