Compare commits
1 Commits
6ddf46e894
...
d9be89c339
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9be89c339 |
20
TODO.md
20
TODO.md
@@ -38,14 +38,18 @@ fmt-check, and commit.
|
||||
exits 75 with a VOID message rather than passing or failing quietly.
|
||||
`--allow-serial-runners` (which keeps the guard and queues) covers the
|
||||
same-checkout overlap that `TMPDIR` scoping cannot; `--allow-parallel-runners`
|
||||
is rejected outright. Both checklists gained the corresponding items, since a
|
||||
half-fix that sets only the cache reads as complete. `GOCACHE` was measured
|
||||
and does not need isolating. Verified with the snippet extracted from the
|
||||
committed document and executed as a consuming repo would adopt it, against
|
||||
paired controls: contamination reproduced on the pre-fix form and absent on
|
||||
the adopted one, retry engaged, exhaustion loud, a genuine finding still
|
||||
reported, and a held host lock failing the pre-fix script while leaving the
|
||||
adopted one untouched.
|
||||
is rejected outright. The stdout and stderr capture files are per invocation
|
||||
rather than per checkout, because serialising the linter does not serialise
|
||||
the shell's redirections: two runs in one checkout — the overlap the flag
|
||||
exists to support — would otherwise truncate and read each other's output,
|
||||
which is the same defect one layer above where it was fixed. Both checklists
|
||||
gained the corresponding items, since a half-fix that sets only the cache
|
||||
reads as complete. `GOCACHE` was measured and does not need isolating.
|
||||
Verified with the snippet extracted from the committed document and executed
|
||||
as a consuming repo would adopt it, against paired controls: contamination
|
||||
reproduced on the pre-fix form and absent on the adopted one, retry engaged,
|
||||
exhaustion loud, a genuine finding still reported, and a held host lock
|
||||
failing the pre-fix script while leaving the adopted one untouched.
|
||||
- 2026-08-09: Kept in-repo agent scratch out of the Docker build context and out
|
||||
of version control. `.claude/` holds one worktree — an entire additional
|
||||
checkout of the repo — per in-flight agent, and under `COPY . .` all of it was
|
||||
|
||||
@@ -766,6 +766,20 @@ style conventions are in separate documents:
|
||||
export GOLANGCI_LINT_CACHE TMPDIR
|
||||
mkdir -p "$GOLANGCI_LINT_CACHE" "$TMPDIR"
|
||||
|
||||
# Capture files, per INVOCATION and not per checkout. Two runs in the same
|
||||
# checkout would otherwise redirect into one pair of fixed paths, opened
|
||||
# O_TRUNC before the linter even starts, and each would print and scan the
|
||||
# other's output — a run reporting a result that is not its own, which is
|
||||
# the whole defect this bullet exists to close, one layer up from where it
|
||||
# was closed. That case is not hypothetical here: two runs in the same
|
||||
# checkout is exactly what --allow-serial-runners below exists to support,
|
||||
# and serialising the linter does not serialise the shell's redirections
|
||||
# or the grep and cat that read them. $$ is the same idiom CHECK_EPOCH
|
||||
# uses, for the same reason.
|
||||
LINT_OUT="$LINT_STATE/run.$$.stdout"
|
||||
LINT_ERR="$LINT_STATE/run.$$.stderr"
|
||||
trap 'rm -f "$LINT_OUT" "$LINT_ERR"' EXIT HUP INT TERM
|
||||
|
||||
# Backstop for a caller that reached the linter without the environment
|
||||
# above. With it set, this should never fire.
|
||||
LINT_MAX_ATTEMPTS=5
|
||||
@@ -774,8 +788,6 @@ style conventions are in separate documents:
|
||||
LINT_VOID_EXIT=75
|
||||
|
||||
golangci_lint_run() {
|
||||
lint_out="$LINT_STATE/run.stdout"
|
||||
lint_err="$LINT_STATE/run.stderr"
|
||||
attempt=1
|
||||
delay=2
|
||||
while :; do
|
||||
@@ -787,7 +799,7 @@ style conventions are in separate documents:
|
||||
# the SAME checkout, which TMPDIR scoping cannot — script/precommit
|
||||
# overlapping a make check is the realistic trigger.
|
||||
golangci-lint run --allow-serial-runners "$@" \
|
||||
>"$lint_out" 2>"$lint_err" || rc=$?
|
||||
>"$LINT_OUT" 2>"$LINT_ERR" || rc=$?
|
||||
|
||||
# Detect the lock collision on the STDERR STREAM, never on the exit
|
||||
# status. Findings are written to stdout and golangci-lint reports
|
||||
@@ -795,16 +807,17 @@ style conventions are in separate documents:
|
||||
# from source cannot be mistaken for a collision and retried away —
|
||||
# that direction would be a false green. The exit status is not a
|
||||
# usable discriminator: the collision exits 3 (exitcodes.Failure)
|
||||
# while findings exit 1, and field reports of 2 mean the value is
|
||||
# not stable across versions.
|
||||
if ! grep -q 'parallel golangci-lint is running' "$lint_err"; then
|
||||
cat "$lint_err" >&2
|
||||
cat "$lint_out"
|
||||
# while findings exit 1, and the other codes in pkg/exitcodes
|
||||
# carry meanings of their own, so no exit status tells a collision
|
||||
# apart from a result.
|
||||
if ! grep -q 'parallel golangci-lint is running' "$LINT_ERR"; then
|
||||
cat "$LINT_ERR" >&2
|
||||
cat "$LINT_OUT"
|
||||
return "$rc"
|
||||
fi
|
||||
|
||||
if [ "$attempt" -ge "$LINT_MAX_ATTEMPTS" ]; then
|
||||
cat "$lint_err" >&2
|
||||
cat "$LINT_ERR" >&2
|
||||
echo "lint: VOID after $LINT_MAX_ATTEMPTS attempts:" \
|
||||
"golangci-lint never acquired its lock, so nothing was" \
|
||||
"analyzed. This is NOT a lint result and no verdict may" \
|
||||
@@ -858,6 +871,21 @@ style conventions are in separate documents:
|
||||
the lock hangs the run instead of failing it; the contending set is
|
||||
bounded to the same checkout, and an eventual result is preferable to a
|
||||
fabricated one.
|
||||
- **Capture stdout and stderr to per-INVOCATION paths, and clean them up.**
|
||||
Two fixed paths under the checkout are one pair for every run in it, and
|
||||
the redirections truncate them before the linter starts, so two
|
||||
overlapping runs print and scan each other's output.
|
||||
`--allow-serial-runners` does not prevent this — it serialises the linter,
|
||||
not the shell — and the overlap it exists to support is precisely
|
||||
`script/precommit` against a `make check` in one checkout. The observed
|
||||
shapes are a run printing the other's `0 issues.` while its own linter
|
||||
found something, and a lock error erased before `grep` reads it, so the
|
||||
retry never fires and the void run returns as a result. Both are a run
|
||||
reporting a result that is not its own, which is this bullet's entire
|
||||
subject reintroduced one layer above where it was fixed. `$$` is
|
||||
sufficient and is the same idiom the `CHECK_EPOCH` rule uses; `mktemp`
|
||||
under the state directory is equally fine. The `trap` matters as much as
|
||||
the paths, or the directory accumulates a pair per run forever.
|
||||
|
||||
Adopting repos must add `.lint-cache/` to both `.gitignore` and
|
||||
`.dockerignore`. The second matters as much as the first: the directory
|
||||
@@ -921,7 +949,9 @@ style conventions are in separate documents:
|
||||
**State the limit of these tests rather than treating them as a guarantee.**
|
||||
They catch contamination that **names** foreign files. They cannot catch
|
||||
contamination that **suppresses** findings through a poisoned entry for
|
||||
colliding content, which has no wall-clock tell either. They are a filter
|
||||
colliding content, which has no wall-clock tell either — **no evidence of
|
||||
that mode has been observed, and nobody should go chasing it**; the point is
|
||||
the reach of the tests, not a claim that the mode exists. They are a filter
|
||||
for the loud mode, not a proof of soundness — which is the whole argument
|
||||
for fixing this in the tooling instead of documenting a discipline that
|
||||
depends on every agent remembering to apply it.
|
||||
|
||||
Reference in New Issue
Block a user