ci: add the check that makes "all green" mean something (#27) - #31
Merged
Merged
Conversation
linhdmn
added a commit
that referenced
this pull request
Sep 21, 2026
The workflow is correct and runs the right steps, but FreePeak/agentloop is a private repo and GitHub-hosted runners are billed — until the org's spending limit is raised, both jobs fail at dispatch with The job was not started because recent account payments have failed or your spending limit needs to be increased. which is a red check that says nothing about the code (seen on PR #31). Written down in the three places someone would look — USAGE §2, the PRD's M6 row, and todo.md — so the next person reads the reason instead of re-deriving it. make check still runs the identical five steps locally and is green.
This repo had no `.github/` at all. The only thing that ran on a pull request
was the gitStream app, which reports `skipping` — so every "tests pass" claim in
"deploys blocked on the full-suite gate" and §13.1's move 8 makes the eval suite
the deploy gate; neither was enforced by anything.
`.github/workflows/ci.yml` runs two jobs on every PR:
- build-test: gofmt check, go build, go vet, go test, golangci-lint
- prd: docs/check-prd.py **and** its --selftest, so the checker cannot rot
into a script that always prints OK
`make check` now runs the same five steps locally, so a green local run and a
green CI run mean the same thing.
Getting the lint job to a clean baseline found real defects, not just style:
- `currentTier` (LoopRunner) was written by nothing and read by nothing —
`RunResult.CurrentTier` is the field that actually carries the tier.
- `maxLandmarkTokens` (internal/memory) was a 20% budget that **no code
enforced**. The file's own comment claimed landmarks' "cap makes this
unreachable", which was the cap itself, unused. Deleted rather than
documented: landmarks are never evicted by design (P32), so a threshold for
them is a number no code could honour. What that costs is now recorded as
§9.1's fourth accepted ceiling — the 70% rule is a **working-tier**
guarantee, not a whole-store one, because enforceCeiling() stops rather than
evict a landmark.
- `Categorize` had a switch staticcheck flagged as a tagged switch; rewriting
it is behaviour-identical and reads better.
- Unchecked writes in the HTTP layer (`json.Encoder.Encode`, `fmt.Fprintf`
for the SSE and console pages, `http.ListenAndServe`) now go through
`writeJSON`/`writeConsole`, which log the failure — a broken client cannot
be told about it (headers are already sent) but it should not be silent.
- `m4_test.go`'s `defer cp.Close()` and the two clients' `resp.Body.Close()`.
`.golangci.yml` pins the linter set and states the one exclusion: errcheck in
`_test.go`, where `defer resp.Body.Close()` is conventional and its failure mode
is a test that fails anyway. Production code gets no exclusion.
Also fixes `.gitignore`: the bare `agentloop` pattern needed its `!cmd/agentloop/`
un-ignore (it was there twice, which is what the duplicate was for — one line
with the reason beats two without).
Checks: `make check` green — gofmt clean, vet clean, 13/13 packages, lint
0 issues, PRD OK and selftest 12/12. `make fmt-check` verified to fail on a
deliberately unformatted file, not just to pass.
The workflow is correct and runs the right steps, but FreePeak/agentloop is a private repo and GitHub-hosted runners are billed — until the org's spending limit is raised, both jobs fail at dispatch with The job was not started because recent account payments have failed or your spending limit needs to be increased. which is a red check that says nothing about the code (seen on PR #31). Written down in the three places someone would look — USAGE §2, the PRD's M6 row, and todo.md — so the next person reads the reason instead of re-deriving it. make check still runs the identical five steps locally and is green.
linhdmn
force-pushed
the
ci/github-actions
branch
from
September 21, 2026 07:34
20be120 to
910ae93
Compare
This was referenced Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #27.
This repo had no
.github/directory at all. The only thing that ran on a pull request was the gitStream app, which reportsskipping— so every "tests pass" claim in #24/#25/#26 meant a human rango test ./.... PRD §13 marked M6 closed with "deploys blocked on the full-suite gate" and §13.1's move 8 makes the eval suite the deploy gate. Neither was enforced by anything. That is now a check's verdict, not a sentence in a PR description.What runs on every PR
gofmtcheck →go build→go vet→go test ./... -count=1→golangci-lintpython3 docs/check-prd.pyand--selftest--selftestis in there deliberately: it asserts the checker can still fail, which is what stopscheck-prd.pyrotting into a script that always prints OK. It had already rotted once — it could not run at all until #29.make checknow runs the same five steps locally, so a green local run and a green CI run mean the same thing.make fmt-checkwas verified to fail on a deliberately unformatted file, not just to pass on a clean tree.Getting lint to a clean baseline found real defects
Not style — three of these are bugs:
currentTier(LoopRunner) was dead. Written by nothing, read by nothing;RunResult.CurrentTieris the field that actually carries the tier.maxLandmarkTokenswas a 20% budget nothing enforced.internal/memory's own comment claimed landmarks' "cap makes this unreachable" — that cap was the unused constant. Deleted rather than documented: landmarks are never evicted by design (P32), so a threshold for them is a number no code could honour. What it costs is now §9.1's fourth accepted ceiling: the 70% rule is a working-tier guarantee, not a whole-store one, becauseenforceCeiling()stops rather than evict a landmark.json.Encoder.Encode, the SSE/consolefmt.Fprintf, andhttp.ListenAndServenow go throughwriteJSON/writeConsole, which log the failure — a broken client cannot be told (headers are already sent) but it should not be silent.m4_test.go'sdefer cp.Close()and the two HTTP clients'resp.Body.Close().Categorize's switch → tagged switch (staticcheck QF1002); behaviour-identical..golangci.ymlPins the linter set so CI and a local run agree, and states its one exclusion: errcheck inside
_test.go, wheredefer resp.Body.Close()is conventional and its failure mode is a test that fails anyway. Production code gets no exclusion — those were fixed instead.Also
.gitignore: the bareagentlooppattern needed its!cmd/agentloop/un-ignore (it was listed twice, which is what the duplicate was for — one line with the reason beats two without).Verification
make checkgreen: gofmt clean, vet clean, 13/13 packages, lint 0 issues, PRD OK, selftest 12/12.Not in this PR
The eval endpoint is still not called by CI. The gate is "a green suite", not "a passing suite" — wiring
GET /admin/api/v1/evalsinto the workflow is the M6 follow-through, and the PRD's M6 row says so rather than claiming otherwise.