All checks were successful
check / check (push) Successful in 22s
The rule landed in 12e8db8 is right; these are the three places where the
canonical text and the repos implementing it can diverge without either
side looking wrong.
1. `.dockerignore` excluding the agent scratch directory is now stated as a
correctness precondition of containerised linting rather than a
context-size measure. `Dockerfile.lint` lints whatever `COPY . .` copies,
and language toolchains discover files by walking the tree instead of
reading `.gitignore`, so a nested worktree in the context puts the
foreign-tree false reds back inside the container — in the convincing
form, where the findings are real but belong to another checkout.
sneak/quak measured the same discovery mechanism taking a test count
from 210 to 1050.
2. The cache-bust build arg is fixed at `CHECK_EPOCH` in `Dockerfile.lint`
as well as in `Dockerfile`. A per-file name is invisible to the grep that
proves every build is busted, which makes a renamed guard and a missing
guard read identically. sneak/quak's lint file currently names it
`LINT_EPOCH`.
3. The formatting check must run in exactly one of the two images, and
either placement is allowed. Splitting lint out of the `Dockerfile` is
precisely the moment `fmt-check` gets dropped from both, and running the
formatter beside the linters is the better shape wherever it is the same
pinned dependency — it takes the last host toolchain off the checked
path for the reason the linter came off it.
Both checklists carry the matching items, since a repo that satisfies the
policy prose but not the checklist is the drift this is meant to stop.
Refs #40
185 lines
12 KiB
Markdown
185 lines
12 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-10: Closed three gaps the containerised-lint rule left between the
|
|
canonical text and the first repos to implement it. `.dockerignore` excluding
|
|
the agent scratch directory is now stated as a correctness precondition of
|
|
that rule rather than a context-size measure: the lint image lints whatever
|
|
`COPY . .` copies, and toolchains discover files by walking the tree instead
|
|
of reading `.gitignore`, so a nested worktree puts the foreign-tree false reds
|
|
back inside the container — `sneak/quak` measured the same discovery mechanism
|
|
taking a test count from 210 to 1050. The cache-bust arg is fixed at
|
|
`CHECK_EPOCH` in `Dockerfile.lint` as well, because a per-file name is
|
|
invisible to the grep that proves every build is busted, making a renamed
|
|
guard indistinguishable from a missing one. And the formatting check is now
|
|
required to run in exactly one of the two images, with either placement
|
|
allowed: splitting lint out of the `Dockerfile` is precisely when `fmt-check`
|
|
gets dropped from both, and running the formatter beside the linters is the
|
|
better shape where it is the same pinned dependency.
|
|
- 2026-08-10: Moved every lint run into a container, on the owner's ruling, and
|
|
made this repo do it rather than merely document it. `script/lint` is now
|
|
`docker build -f Dockerfile.lint .` and nothing else; the linter is never
|
|
installed on the host and never invoked there, so a run cannot inherit another
|
|
checkout's content-keyed result cache, the host-global
|
|
`$TMPDIR/golangci-lint.lock`, or a host toolchain that differs from the pinned
|
|
one — the three mechanisms behind a confirmed false green, a string of
|
|
findings reported against other agents' checkouts, and a container that saw
|
|
thirteen findings the host missed. Linting runs as a build step, so a
|
|
successful build is a clean lint, which also works where the docker daemon is
|
|
remote and bind mounts are impossible. The recursion this creates is resolved
|
|
by direction rather than by detection: the main `Dockerfile` runs the
|
|
individual non-lint checks instead of `make check`, and `script/cibuild` runs
|
|
`script/lint` first, so no build ever nests a build. `Dockerfile.lint` carries
|
|
the same `CHECK_EPOCH` guard as the main image, with the `ARG` below the
|
|
dependency layer so only the lint steps re-run — blanket `--no-cache` was
|
|
rejected because it makes every lint reinstall its dependencies over the
|
|
network. Two canonical forms were superseded rather than left standing beside
|
|
the new one, since consuming repos read this document literally: the
|
|
`script/bootstrap` golangci-lint install (nothing runs a host linter now, so
|
|
it can only reintroduce skew; the version-enforcement principle stays
|
|
documented for other pinned host tools) and the per-checkout
|
|
cache/lock/`.lint-cache` wrapper (its whole subject was making a host run
|
|
trustworthy). The Go multistage lint stage goes with them: it ran `make lint`,
|
|
which is now a docker build. `golangci-lint config verify` was kept on
|
|
measurement, not preference — a bogus config key passes `golangci-lint run`
|
|
with `0 issues` and fails `config verify`, and every case reproduced
|
|
byte-identically under `docker run --network none`, so the schema is embedded
|
|
in the pinned binary and the line costs no network. Verified with two
|
|
consecutive runs on an unchanged tree both executing the linter, a planted
|
|
violation caught and reverted, the bare-build guard firing, and the main image
|
|
building without attempting a nested build.
|
|
- 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).
|