Repository navigation
Resolve #95, #97, #98 — toolchain floor, CodeRabbit findings, gate guidance - #110
Merged
Merged
Conversation
Closes #97 (resolution A). `main` could not build on OTP 26: `mix.lock` resolves `cowlib` 2.20.0, required by `cowboy` 2.19.0, and `cow_capsule.erl` calls `parse/1`, which OTP 26 does not provide. `README.adoc` and `docs/AI_INSTALLATION_GUIDE.adoc` both promised "OTP 26 or newer", so an AI or a stranger following the documented prerequisites got a compile failure instead of a working install — which bears directly on the "install hyperpolymath's FoT please" goal. This is the support-policy call the issue asked for, resolved as option A: raise the floor rather than pin `cowlib` back to 2.16.0. The toolchain the repo already pins (mise.toml: erlang 27.0, elixir 1.19.5-otp-27) is OTP 27, and holding `cowlib` at 2.16.0 to keep a floor nothing else targets would have frozen out modern cowboy/bandit fixes. Changed together in one commit, so no reader sees a stale floor: * .github/workflows/elixir-ci.yml — matrix is now 1.17, 1.18 and 1.19 on OTP 27. The three OTP-26 cells are gone; every remaining cell is a measurement of a version the documents promise. Dialyzer moves from 1.17/26 to 1.19/27 for the same reason. * elixir-mcp/mix.exs — `elixir: "~> 1.15"` to `"~> 1.17"`, matching the floor the matrix measures. * README.adoc, docs/AI_INSTALLATION_GUIDE.adoc, elixir-mcp/README.adoc — the floor, and every "older than" fallback sentence that quoted it. * 0-AI-MANIFEST.a2ml — value strings only: "Elixir 1.15+"/"OTP 26+" to "Elixir 1.17+"/"OTP 27+". Grammar untouched, per the standing ruling. * Justfile — `just doctor` reports and enforces the new floor. * mise.toml — the comment no longer claims the matrix covers OTP 26. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Contributor
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Two findings from #95, in dependency order: F3 gates F1, because a security fix verified by a miscounting harness is unverified. ## F3 — the harness counted the wrong thing, and had never been seen to fail `scripts/consent_bypass_check.sh` counted *every* `gh` invocation, so its verdict depended on whether the engine happened to probe `gh auth token` rather than on whether it sent anything. A harness cannot pass or fail for a reason unrelated to the property it measures. * Only `gh issue create` is counted now. A credential probe is not a send. * New `--self-test` positive control: the script rewrites the consent check to `true`, rebuilds, and requires itself to FAIL against that build — then restores the tree and re-runs against the real gate. If a mutant with a defeated gate walks through unchallenged, the harness fails loudly. The mutation is anchored to an exact marker string, and the self-test refuses to run if that marker has moved, so it cannot quietly assert nothing. Verified locally with a stubbed builder: clean build PASSes, mutant FAILs, tree restored. * Wired into CI (`--self-test`) and into `just consent`. ## F1 — `:human_confirmed` was a forgeable atom `Submitter` selected the credentialed branch on `Keyword.get(opts, :consent) == :human_confirmed`. Any code in the VM could name that atom, and the check ran on data the caller supplied — the estate's own doctrine, a default is not a gate, applies verbatim: a claim is not a proof. New `FeedbackATron.Consent` mints what the send path now requires: * **Unguessable** — 256 random bits, so no wire client can mint or guess one. The MCP door and the HTTP door are now structurally incapable of asserting consent, which is what the pre-ledger rule always claimed. * **One-time** — redeeming deletes it, so it cannot be replayed. * **Payload-bound** — issued over a digest of the exact report, destinations and labels that were shown, so a leaked value cannot send something else. * **Expiring** — unspent capabilities die inside five minutes. `CLI` mints the capability at the terminal, in the process that asked the question, and carries no claim about it into the opts. Fails closed: if the service is unreachable the answer is an error, never consent. **No `--yes` flag is added.** Non-tty still means no. **Stated residual:** code already executing inside the VM can call `Consent.issue/2` directly — and such code can equally call `Channel` straight. This removes *data* as a way to assert consent, which is the way that mattered, because it is the one a remote client could reach. ## Tests * `consent_test.exs` (new): one-time spend, payload/destination/label binding, expiry (backdated via `:sys.replace_state`, so no sleeping), refusal of every value a caller could invent, and fail-closed behaviour. * `submitter_test.exs`: four forgery tests — the bare atom, a fabricated token, a real capability minted for a different payload, and one minted for other destinations. **Each fails against today's `main`.** Plus: a real capability opens the send path exactly once, and cannot carry a batch. * "Opened the send path" is measured without a network call: the destination is a platform no credential can resolve, so the attempt dies at `Credentials.get/2` and reports `:no_credentials` — a different outcome from drafting, and the only one that proves the gate opened. This matters in CI, where `GITHUB_TOKEN` is present in the environment. * `cli_test.exs`: a yes now attaches a capability that redeems once and does not redeem twice, and a yes with the service unreachable sends nothing. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
arena-ai-coding-agent
Bot
force-pushed
the
arena/01a0edde-feedback-o-tron
branch
2 times, most recently
from
September 29, 2026 16:27
ede91d1 to
332233b
Compare
…F2)
`Doors.http_children/1` probed the port with `:gen_tcp.listen` + close, and
dropped the Bandit child when the probe failed. Two defects in one shape:
* **A check-then-bind race.** The probe closed its socket before Bandit
bound, so whatever passed the probe could still be taken — and whatever
took it afterwards made Bandit fail on a port the probe had approved.
* **A silent, permanent failure.** `serve --no-stdio` with the port taken
lost the only door it had and then slept forever: healthy-looking, serving
nothing, with one warning on stderr nobody reads.
Now: the child asked for is the child handed back, and a bind failure
propagates out of the supervision tree to `CLI`, which reports it and exits
non-zero. The check-then-bind race is gone because the probe is gone —
Bandit binds exactly once.
* `Doors.children/1` always returns the Bandit child when `http: true`.
* `CLI.run/2` handles `{:error, {app, reason}}` from
`Application.ensure_all_started/1` instead of crashing on a failed match
(the old `{:ok, _} =` produced an opaque `CaseClauseError`). It names the
cause and adds a hint for `:eaddrinuse` and `:eacces`, suggesting
`serve --http-port N` — without ever dropping the term itself.
Tests, replacing the one that asserted the old silent behaviour:
* `doors_test.exs` — the child is handed back whether the port is free or
taken, and starting it against an occupied port returns `{:error, reason}`
naming `Bandit` and `eaddrinuse`.
* `escript_smoke_test.exs` — end to end against the real binary:
`serve --no-stdio` on an occupied port **exits non-zero within 30 s** and
says why. It failed by sleeping before; the test now fails by timing out,
so the regression cannot come back quietly.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…o fail (#95 F4, F5, F6, minors) ## F4 — `stapeln.toml`: the production target declared no HTTP settings A deploy of the production target started a release with every door shut: nothing listening, and nothing saying so. It now declares the intake door open on the engine's own port, 7722 — deliberately not an 8080-class port. The bind stays on `127.0.0.1`, and the comment says why: the HTTP intake carries no authentication of its own, it is a loopback wire for a cartridge or sidecar in the same network namespace. Widening the bind to make wiring easier would publish an unauthenticated door, so the comment names the supported way to reach it from outside (an authenticating proxy in front). ## F5 — Step 20.7 could not fail `docs/superpowers/plans/…adoc` ended the credential step with a `--dry-run` submit. A dry run returns the preview before `Credentials.get/2` is reached, so on a host with no `oauth_token` in `hosts.yml` a broken `gh auth token` fallback produced exactly the same green output as a working one. The step now reads the credential the send path would use, via `mix run`, and requires `{:ok, _}` — never `{:error, :no_credentials}` — which reaches `gh_auth_token/1` and files nothing. It must be run in both states (with `hosts.yml`, and with it moved aside), because each state measures a different branch. The amendment is marked with its date and reason. ⚠ Owner-only rehearsal: this is an edit to the plan text. No agent runs it. ## F6 — the privacy watch-list omitted the owner's own example The section's prose forbids sending "a detailed build number, a MAC address or an IP address", and the `Look for` table beneath it had no MAC-address row — so a rehearser following the table as instructed would have passed a payload the prose forbids. MAC row added, with the same "network identity" reasoning as the IP row. The credential row now covers `github_pat_…` alongside `ghp_…` and `gho_…`; fine-grained tokens match neither old prefix. ## Minors * `0-AI-MANIFEST.a2ml:118` (and the same class of error at :163) — the executable path is `elixir-mcp/feedback-o-tron`, not `./feedback-o-tron`. An AI reading the manifest was told the wrong binary, which bears directly on "install hyperpolymath's FoT please". Value strings only; grammar untouched, per the standing ruling. * `README.adoc` — the 401 row now names the stale-environment-token case: an exported `GITHUB_TOKEN`/`GH_TOKEN` beats the gh CLI, so `gh auth login` can succeed while the stale variable keeps being sent. The "After Install" commands now use the installed name on PATH rather than a `./` path that only works inside `elixir-mcp/`. * `.github/CONTRIBUTING.md` — the verification commands are now commands that exist: `just build`, `just lint`, `just test`. `just check` was not a recipe, and the "cargo check" comment belongs to a Rust project. * `llm-warmup-{dev,user}.adoc` — `just deps` fetches dependencies; it does not compile them. Both guides said "fetch and compile". Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
hyperpolymath
approved these changes
Sep 29, 2026
hyperpolymath
marked this pull request as ready for review
September 29, 2026 16:52
hyperpolymath
approved these changes
Sep 29, 2026
arena-ai-coding-agent Bot
added a commit
that referenced
this pull request
Sep 29, 2026
PR #110 merged and CI ran for real on `main`. Result: * `Compile (warnings as errors)` — green on all three cells * `Dialyzer` — green * `Run tests` — **red on all three cells**, one failure: `test run/2 confirm gate a yes with the consent service down sends nothing and exits non-zero (FeedbackATron.CLITest)` Exit code 2 means failures **and** compile warnings, so there are two things to read. This PR adds the relay so the failure is readable at all: log archives are hosted on `results-receiver.actions.githubusercontent.com`, which is not reachable from the sandbox, so a red cell cannot be diagnosed from the log the way a human would. The step re-emits the failing block (and the compile warnings) as a check annotation, which the Checks API exposes. No behaviour is changed by this PR. Next: read the annotation, fix the test, then decide whether the relay stays. Co-authored-by: Arena Agent <agent@arena.ai> Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
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.
Work in progress. Landing in reviewable slices; each acceptance criterion is met by a named commit and backed by a green cell.
Closes
maindoes not build on OTP 26 while the README and the AI install guide promise it. Resolved as option A (raise the floor to OTP 27)..deedwhile the Mustfile critical rule requires0-AI-MANIFEST.a2ml.Slices
fix(build)— raise the floor to Elixir 1.17 / OTP 27 (main does not build on OTP 26 while README and the AI install guide promise it #97).fix(harness)— repairconsent_bypass_check.shand give it a positive control (F3), which gates F1.fix(security)— replace the forgeable consent atom with an unforgeable capability (F1).fix(doors)— stop silently dropping the HTTP door when the port is taken (F2).fix(config+docs)— F4, F5, F6 and the four minors.chore(ci)— align the repo with the canonical rsr-template-repo.Out of scope
📛 Hands off
.a2mlgrammar (standing ruling). No bulk rename of manifests; manifest edits are value strings only. No--yesflag is introduced anywhere.⚠ No agent runs the owner-only stranger rehearsal; where #95 touches that document, the change is to the plan/step text only.