All checks were successful
check / check (push) Successful in 7s
A golangci-lint result on a host running many concurrent workers does not reliably belong to the tree that asked for it. Two independent mechanisms, which have repeatedly been mistaken for one: The result cache is keyed on file content, not location, so two checkouts of the same commit hold byte-identical files, share cache entries, and one tree's findings are served for the other under the other tree's path. This produced a confirmed false green as well as the loud false reds. Moving workers from shared worktrees to their own clones does not address it — two clones collide exactly as two worktrees did — and removes only the foreign-path artefact that made the defect noticeable. The concurrency lock is $TMPDIR/golangci-lint.lock (pkg/commands/run.go, acquireFileLock), host-global and independent of GOLANGCI_LINT_CACHE, with a five-second acquire timeout, so it fails when the host is busiest. A private cache directory does not isolate it. Setting only the cache closes the contamination half and leaves runs failing red on a condition that is not a result at all. REPO_POLICIES.md now carries the canonical Go script/lint: both variables scoped into a .lint-cache/ directory inside the checkout, above any container-versus-host branch so every path reaching the linter gets them; --allow-serial-runners, which keeps the mutual-exclusion guard and queues rather than aborting, for the same-checkout overlap TMPDIR scoping cannot cover, with --allow-parallel-runners rejected because it deletes the guard; and a bounded retry that treats the lock error as VOID rather than as findings, exiting 75 on exhaustion so it is neither a pass nor a failure. Detection is on the stderr stream and never on exit status: findings go to stdout, so a finding quoting the lock message in source cannot be retried away, and the exit status is not a stable discriminator anyway. The interim void rule is recorded with the ../ clause that the original filter missed, and with its limit stated — it catches contamination that names foreign files, not contamination that suppresses findings. Both checklists gained the corresponding items, since a half-fix that sets only the cache reads as complete. GOCACHE was measured rather than assumed and does not need isolating: with the two variables scoped per checkout and GOCACHE shared at the host default, each checkout reported its own paths. Verified with the snippet extracted from the committed document and executed as a consuming repo would adopt it, each control paired against the pre-fix form: contamination reproduced on the pre-fix script and absent on the adopted one; a stub linter colliding twice then clearing, with the retry engaging and succeeding; exhaustion exiting 75 with a VOID message; a genuine finding whose text quotes the lock message reported as findings with no retry; and a real held lock failing the pre-fix script with exit 3 while the adopted script, inheriting the same environment, completed in one second.
138 lines
8.5 KiB
Markdown
138 lines
8.5 KiB
Markdown
# Workflow
|
|
|
|
- branch (from `main`)
|
|
- do the work in Next Step
|
|
- move Next Step to the top of Completed Steps
|
|
- move the top item of Future Steps into Next Step
|
|
- commit (`TODO.md` changes in the same commit as the work)
|
|
- merge to `main` if the branch is not protected, otherwise open a PR
|
|
- push
|
|
|
|
# Status
|
|
|
|
pre-1.0
|
|
|
|
# Next Step
|
|
|
|
Finish the two draft prompt documents in the working tree and commit them:
|
|
prompts/FIXUP_CLEAN.md (currently a near-empty stub) and prompts/FIXUP_REPORT.md
|
|
(a rough draft). Write the missing content, run `make fmt` so they pass
|
|
fmt-check, and commit.
|
|
|
|
# Completed Steps
|
|
|
|
- 2026-08-09: Made a golangci-lint result belong to the tree that asked for it.
|
|
REPO_POLICIES.md now carries the canonical Go `script/lint`, which gives the
|
|
linter per-checkout `GOLANGCI_LINT_CACHE` and per-checkout `TMPDIR`. The two
|
|
are separate defects and the second is the one that gets dropped: the result
|
|
cache is keyed on file content rather than location, so checkouts holding
|
|
identical files serve each other's findings under the other's path, while the
|
|
concurrency lock is `$TMPDIR/golangci-lint.lock` — host-global, independent of
|
|
the cache, and unaffected by isolating it. Moving workers from worktrees to
|
|
their own clones does not help either half; it only removes the foreign-path
|
|
artefact that made the defect visible. The lock error is retried rather than
|
|
surfaced, because it is not a result: it exits non-zero exactly as findings
|
|
do, and reporting it as findings sends a correct branch back for rework.
|
|
Detection is on the stderr stream and never on exit status, so a finding
|
|
quoting the lock message in source cannot be retried away, and exhaustion
|
|
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. 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
|
|
reaching the image: another session's unreviewed, sometimes uncommitted work,
|
|
inflating the context by a multiple of the repo and invalidating `COPY` for
|
|
reasons unrelated to the repo's own content. The `.dockerignore` entry is
|
|
root-anchored, because the directory occurs exactly once where agents run at
|
|
the repo root and the `**/` form additionally deletes any nested directory of
|
|
that name — with the residual gap that follows from anchoring (a monorepo
|
|
running agents in subdirectories still ships `services/api/.claude/`) stated
|
|
in the canonical `.dockerignore`, the policy and the existing-repo checklist,
|
|
since consuming repos receive the files rather than the tracker; the
|
|
`.gitignore` entry is unanchored, because `.gitignore` patterns already match
|
|
at every depth, and each file is written to its own semantics rather than
|
|
derived from the other. Also closed the consequence that ships broken
|
|
silently: excluding `.git` means `git describe` cannot run in any build stage
|
|
and yields an empty version without erroring, so `script/docker` and
|
|
`script/cibuild` now compute the version on the host and pass
|
|
`--build-arg VERSION`, and `REPO_POLICIES.md` states where `VERSION` comes
|
|
from instead of leaving the reader to fill the gap with `git describe` inside
|
|
the build. The two Go documents that carry the `GOLDFLAGS` pattern were
|
|
corrected in the same pass, from `:=` to `?=`, since a `$(shell git describe)`
|
|
evaluated inside a build stage is exactly the empty version this closes.
|
|
Verified by enumerating a probe image before, after, and against the
|
|
`**/`-prefixed form, with a positive control and the `CHECK_EPOCH` cache
|
|
verification re-run under the changed build context.
|
|
- 2026-08-09: Closed the secret exposure in the canonical `.dockerignore`: a
|
|
developer's local `.env`, `*.pem` or `*.key` was reaching the Docker build
|
|
context under `COPY . .`, invisible to every git-based check because
|
|
`.gitignore` covers it. The patterns are written to `.dockerignore`'s own
|
|
`moby/patternmatcher` semantics — `**/`-prefixed so they hold at every depth,
|
|
which also fixes nested `node_modules` — rather than transplanted from
|
|
`.gitignore`, whose unprefixed form protects only the repository root while
|
|
reading as solved. Coverage extends past the `.env`/`.pem`/`.key` trio to the
|
|
`prod.env` convention, `.envrc`, PKCS#12 bundles and extensionless SSH keys,
|
|
every one of them case-folded with character ranges because matching is
|
|
case-sensitive and an ALL-CAPS twin per pattern still misses `Server.Key`.
|
|
`REPO_POLICIES.md` and both repo checklists now state that asymmetry and
|
|
require verification by enumerating the image rather than by reading the
|
|
patterns. Verified with a probe image before, against three naive forms
|
|
(unprefixed, lowercase-only, ALL-CAPS-doubled), and after.
|
|
- 2026-08-09: Made the pinned golangci-lint actually propagate: REPO_POLICIES.md
|
|
now carries the canonical `script/bootstrap` snippet for Go repos, which
|
|
installs when the installed version does not match the pin (the old
|
|
`if missing` guard tested PATH presence only, so pins were inert on any
|
|
provisioned machine and CI silently disagreed with local) and then re-resolves
|
|
the binary through `PATH` and fails loudly, naming the shadowing path, when
|
|
the install did not take effect — the failure mode the naive
|
|
compare-then-install fix leaves behind while reporting success.
|
|
- 2026-08-09: Fixed the false green in the canonical CI gate: `script/cibuild`
|
|
and `script/docker` now pass a per-invocation `CHECK_EPOCH` nonce, and the
|
|
`Dockerfile` (plus the Go multistage template in REPO_POLICIES.md, in both its
|
|
lint and builder stages) declares `ARG CHECK_EPOCH` with a guard that makes a
|
|
bare `docker build .` fail closed. Corrected the org-canonical text that
|
|
asserted a successful build implies all checks pass, across every document
|
|
carrying it: `REPO_POLICIES.md`, both repo checklists (which still told agents
|
|
to write the pre-fix `script/cibuild` and ended on an acceptance item the
|
|
guard makes unsatisfiable), and the Go styleguide.
|
|
- 2026-08-07: Set the canonical `.golangci.yml` to the org-standard v2-schema
|
|
config already deployed byte-identical across the org's Go repos (settings
|
|
under `linters.settings` so thresholds like lll/funlen/cyclop/dupl actually
|
|
apply under golangci-lint v2). Recorded the canonical golangci-lint version
|
|
(v2.12.2, commit-pinned) in REPO_POLICIES.md.
|
|
- 2026-03-20: Strengthened constructor naming and Params struct rules in the Go
|
|
styleguide.
|
|
- 2026-03-18: Documented fail-fast Dockerfile lint stage and conditional -v test
|
|
rerun patterns in REPO_POLICIES.md.
|
|
- 2026-03-11: Added HTTP service hardening policy for 1.0 releases.
|
|
- 2026-03-10: Added policy: no build artifacts in repos.
|
|
- 2026-03-04: Added LLM prose tells reference and copyediting checklist, then
|
|
several self-applied revision passes.
|
|
- 2026-02-28: Expanded the pre-1.0 schema migration rule; added clawpub
|
|
reference.
|
|
- 2026-02-23: Added Go style rules (no type-only packages, Stringer for
|
|
string-based types); template repos section in README.
|
|
- 2026-02-22: Initial policy corpus: REPO_POLICIES.md, code styleguides
|
|
(general, Go, JS, Python), repo checklists, CI policy, hash pinning, Go HTTP
|
|
server conventions, repo scaffolding.
|
|
|
|
# Future Steps
|
|
|
|
- Finish, format, and commit FIXUP_CLEAN.md and FIXUP_REPORT.md (the Next Step).
|
|
- Commit this TODO.md at the repo root; it is the last missing policy file.
|
|
- Decide the fate of untracked resume.sh: commit it or delete it.
|
|
- Add more prompt templates for common development tasks (from README TODO).
|