fix(pull): do not self-heal the .gitignore under --dry-run - #944
Conversation
`refreshTeamRepo` self-heals an older `.teamai/.gitignore` -- one that still ignores a bare `env` (pre-beta.5). That rewrites a *tracked* file in the member's own checkout, and `pull` reaches the call above every dry-run guard: the function takes no options, so it cannot tell a preview from a real run. Thread `options.dryRun` into the refresh and gate the self-heal on it. The migration is idempotent, so the next real pull still performs it. Measured with the CLI on a self-mode clone whose `.teamai/.gitignore` is the pre-beta.5 shape and is committed: before pull --dry-run .teamai/.gitignore sha256 ed747943d24dc463 after pull --dry-run .teamai/.gitignore sha256 f572d520732a16c2 Whole-fixture tree diff: exactly one path changed, and it was that file. This is the one finding from the review on Tencent#866 that I could still reproduce on main today; the empty `locks/` directory it also reported is fixed in Tencent#896.
The earlier dry-run |
Resolve refreshTeamRepo's options: keep inline/fetchTimeoutMs and add main's dryRun guard for the self-mode .gitignore self-heal (Tencent#944).
Real-CLI verification for the
|
base 509a8135 |
fix 7559d8d3 |
|
|---|---|---|
.teamai/.gitignore sha256 before |
ed747943d24dc463… |
ed747943d24dc463… |
.teamai/.gitignore sha256 after |
f572d520732a16c2… |
ed747943d24dc463… |
| rewritten? | true | false |
| whole-tree rewritten paths | ["app/.teamai/.gitignore"] |
[] |
git status --porcelain after |
M .teamai/.gitignore |
`` (clean) |
| CLI exit / signal | 0 / none | 0 / none |
Content after the run — base: "token\nteamai.lock\nenv.local\nlearnings-wt/\n.learnings-lock\npending-learnings/\nusage.jsonl.*\nusage.pending-*.jsonl\nconfig.yaml.*.tmp\n"; fix: "token\nenv\nenv.local\n" (byte-identical to the pre-image). Both runs printed Team repo: single-repo (knowledge on main), so both reached the self-mode branch — the guard, not an early exit, is what changed the outcome.
Bridging this to main. The squash commit b0ce1f22 has parent bae48e5c, not the base this PR was opened on (83228692) — main moved while the PR sat, and GitHub applied the PR's three hunks onto the newer parent. Main's pull.ts is therefore 5acff3c6, not the blob I pushed, and it cannot be rebuilt in the tree I measured: it imports instructionFileInstallProbe / writesInstructionBlock, which upstream added to rule-format.ts after my base. Rather than hand-wave that gap, the four regions this run exercises were compared literally against main:
- the doc-comment paragraph at
src/pull.ts:83 - the
options: { dryRun?: boolean } = {}parameter at:93 - the guard plus dynamic import at
:121 - the call site
refreshTeamRepo(localConfig, options)at:926
All four are byte-identical in main and in the copy that was run. bae48e5c...b0ce1f22 records exactly +15/-5 for pull.ts, matching those hunks. The test file is byte-identical as well (715fc4d2116a81ac6df146221f1d55374f68532e on main == the local copy).
So the shipped code leaves the tracked file alone under --dry-run; the earlier rewrite is gone from base → fix, measured through the real CLI rather than through the harness.
What
pullrefreshes the team repo before it does anything else. In self mode thatrefresh calls
migrateSelfModeGitignore, which rewrites.teamai/.gitignore— atracked file in the member's own checkout — when it still has the pre-beta.5
shape (a bare
envline that keeps team env vars off main).refreshTeamRepo(localConfig)takes no options, so it cannot tell a preview froma real run, and
pullreaches it above every dry-run guard.pull --dry-runtherefore writes.
The refresh now takes
optionsand runs the self-heal only when the run is real.The migration is idempotent, so the next real pull performs it.
Evidence
Real CLI, self-mode clone,
.teamai/.gitignore=token\nenv\nenv.local\n,committed to the checkout:
.teamai/.gitignoresha256pull --dry-runed747943d24dc463…pull --dry-runf572d520732a16c2…Whole-fixture tree hash: exactly one path differed —
app/.teamai/.gitignore—and nothing was removed.
pullreported no error.Test
dry-run-load-path.test.tsalready asserts, for a fresh self-mode clone, that"nothing that already existed may be rewritten, whatever it is". Its fixture
(
setupSelfModeClone) has no.teamai/.gitignoreat all — and the migrationreturns early when the file is missing — so the one path the refresh could write
to was the one path that fixture could not reach.
The new case adds the file to that same fixture and asserts the same thing:
pull.ts(blob)509a81351 failed / 94 passed7559d8d395 passed / 0 failedRelation to #866
This is the one
[P1 blocking]finding from the review on #866 that I could stillreproduce on
maintoday. The lock-directory half was fixed in #896, and thequeued-learning publish is gated there as well. I have not re-measured the
remaining two.
The line is the one the review pointed at:
refreshTeamRepo→migrateSelfModeGitignore(now atsrc/pull.ts:112).