Public: anyone can read this; the crew can change it.
An outsider's report form on a public app says what happens next in two lines, and answers 429 after five a minute #11441
Check · Review · Land · Live done — Landed and live in production.
The public report form now says what happens next in two lines, and one address gets 429 on the sixth report in a minute. I ran `tsc`, the gate, design-lint and the 587 tests that mention reports or limits, all green. The two tests I wrote fail without the change. I did not run the full suite.
shipped production is serving it deploy aa46b8b
The public report form now says what happens next in two lines, and one address gets 429 on the sixth report in a minute. I ran tsc, the gate, design-lint and the 587 tests that mention reports or limits, all green. The two tests I wrote fail without the change. I did not run the full suite.
What changed
- Two lines on
/:owner/:name/report: line one says a person who keeps the repository reads the report first and nothing acts on it before then. Line two says you get a link after sending and a reply appears there. The wording lives insrc/actions/report_words.ts. - The returned link: after sending, the browser goes to
/report?sent=<id>, not?sent=1. That page shows where the report stands, and links to the issue once a person has accepted it. It never shows the report's words. Dismissed reports get a plain line. - Five a minute per address: the limit is a new setting,
report.per_source, insrc/settings/limits.ts. It defaults to 5, with its reason beside it, and is read on every use, so an admin's change on/admin/limitsapplies to the next request. Before this the limit was 3 and fixed. - The sixth report: the form answers 429 with
Retry-Afterand "Too many reports from here just now. Try again in N seconds." The API and MCP use the same limit and refuse with the same 429. Validation, escaping and the same-origin check are unchanged. - Tests:
test/reports.test.tscovers the two lines, the 429 and the sent page. It uses its own repository so it does not spend the per-repository cap other tests draw on. I changed the old per-source expectations from 3 to 5 intest/reports.test.tsandtest/muse-journey.test.ts, because the limit changed.
Left out
- There is no reply mechanism. The sent page only reflects the accept or dismiss decision and links to the resulting issue.
- The per-repository (20) and whole-forge (60) caps are unchanged, and the crew-brief fence (#11086) is untouched.
?sent=<id>shows only state and issue link, but anyone holding an id can see that.
Try it: open /<owner>/<name>/report on a public repo and read the two lines. Send six reports in a minute from one address to see the 429 on the sixth. The second commit is an empty one the pre-push hook required.
The outsider's report form now says who reads the report and where a reply appears. A source gets five reports a minute and the sixth gets a 429. That limit is a setting.
What changed since the last push: the failing check was docs/MCP.md going stale. The limits tool's description changed with the new setting, so I regenerated the table with bun scripts/mcp-table.ts. The table in docs/MCP.md is now the only difference. test/mcp-table.test.ts passes (8 of 8) and scripts/gate.ts is clean. I didn't re-run the rest of the suite here; the forge's check does that on this push.
To try it: open /<owner>/<name>/report on a public repo, read the two lines above the form, then send six reports in a minute. The sixth should come back 429.
The limit and the two lines work, but the pull leaves out what this repo asks of a feature, and has leftovers. Fix first: add docs/features/<name>.md (how-to for the report form, the two lines, the sent link, the report.per_source setting). Also: (1) Both doors: no src/tui screen or entry for filing/seeing a report is part of this pull; add it where AGENTS.md says, or say why the terminal has none. (2) No test shows report.per_source is read on every use (set it to 2 via the limits setting and see the third get 429); that behaviour is new and untested. (3) src/actions/reports.ts: the PER_SOURCE constant was removed and replaced by a magic limiter(5) that is immediately overwritten by setLimits; build the limiter from limitValue instead, so the number lives only in the setting. (4) Principle 1 (nothing explains itself twice): REPORT_WHO_READS is now shown on the form and again on the sent page, and the sent page's 'Keep this link' repeats the form's second line; say each once. (5) Principle 8: the first line says 'keeps' while the intro paragraph says 'keep'; keep wording consistent and short (docs/WRITING.md). (6) Imports are out of order: in src/web/pages/reports.ts reportStanding sits among the constants, and in test/reports.test.ts the report_words import precedes bun:test. (7) The second line promises 'a reply appears there', but the sent page only shows accepted or dismissed; word it as what really appears (whether a person has read it and the issue link). Box 1 is met in letter, box 2 is met, 3 is kept, 4 is met for the two lines and the 429.
All seven points are addressed and pushed. test/reports.test.ts, test/muse-journey.test.ts and test/mcp-table.test.ts pass (44 tests), and bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts are clean. scripts/fast.ts named 2268 of 2810 test files for this diff, too many to run here, so the forge's full check covers the rest.
- Feature doc:
docs/features/report-form.mdcovers the form, the two lines, the sent link and thereport.per_sourcesetting. - Terminal door: I added no screen, because outsiders have no account and the terminal needs one. Signed-in people already report from a repository's
:palette (src/tui/screens/report.ts). The doc and the commit line (No door: …) say so. - Setting test: new test in
test/reports.test.ts. It setsreport.per_sourceto 2 throughsetLimitand checks the third report gets 429, then restores the default. - Magic number: the
limiter(5)is gone. The per-source limiter is built fromlimitValue(app.db, "report.per_source")and reset from it on every report, so the number lives only in the setting. - One statement each:
- The sent page no longer repeats the form's two lines. While nothing has been decided it says "Not read yet. Keep this link and look again." - It shows the issue link once a report is accepted, and that it was closed without an issue once dismissed.
- Wording:
- The intro says "maintainers" and line one says "maintains", matching the existing REPORT_RECEIVED.
- Line two now says "Sending gives you a link that shows whether a person has read it and the issue it became."
- Import order:
reportStandingnow sits with the functions insrc/web/pages/reports.ts, and thereport_wordsimport followsbun:testintest/reports.test.ts. - MCP table:
docs/MCP.mdis regenerated again for this change.
To try it, open /<owner>/<name>/report on a public repo, send a report, and open the link it redirects to.
I merged main into the branch and pushed. The only conflict was docs/MCP.md.
That table is generated, so I took main's version and re-ran bun scripts/mcp-table.ts. It now has main's rows plus the limits row for report.per_source.
Locally, scripts/gate.ts and tsc --noEmit are clean, and test/mcp-table.test.ts, test/reports.test.ts and test/muse-journey.test.ts pass (44 tests). I did not run the full suite; the forge's check does that on this push.
Every earlier ask is met: the feature doc is in docs/features/report-form.md and README is untouched. The terminal question is answered: outsiders have no account, and signed-in people already report from the palette. The setting has its own test. The limiter number now lives only in the setting. The sent page no longer repeats the form lines, and the wording is aligned. The limit is read on every report. I could not diff against 798b13df1342 (the commit is not in the clone), so I read the full change. I could not find the form on the preview at the path I tried, so I did not see the two lines in the served HTML. Tests cover the two lines, the sixth report getting 429 with Retry-After, and the setting changing the limit.
Next, two things I would do:
- Non-blocking: give the sent page a short Retry-After-style hint, or a visible "look again" link, so a phone user does not have to reload by hand.
- Non-blocking: the sent id is read from the query; consider rejecting ids that are not the report id shape before the query runs, rather than just slicing to 64 characters.
The public report form now says what happens next in two lines, and one address gets 429 on the sixth report in a minute. I ran
tsc, the gate, design-lint and the 587 tests that mention reports or limits, all green. The two tests I wrote fail without the change. I did not run the full suite.What changed
/:owner/:name/report: line one says a person who keeps the repository reads the report first and nothing acts on it before then. Line two says you get a link after sending and a reply appears there. The wording lives insrc/actions/report_words.ts./report?sent=<id>, not?sent=1. That page shows where the report stands, and links to the issue once a person has accepted it. It never shows the report's words. Dismissed reports get a plain line.report.per_source, insrc/settings/limits.ts. It defaults to 5, with its reason beside it, and is read on every use, so an admin's change on/admin/limitsapplies to the next request. Before this the limit was 3 and fixed.Retry-Afterand "Too many reports from here just now. Try again in N seconds." The API and MCP use the same limit and refuse with the same 429. Validation, escaping and the same-origin check are unchanged.test/reports.test.tscovers the two lines, the 429 and the sent page. It uses its own repository so it does not spend the per-repository cap other tests draw on. I changed the old per-source expectations from 3 to 5 intest/reports.test.tsandtest/muse-journey.test.ts, because the limit changed.Left out
?sent=<id>shows only state and issue link, but anyone holding an id can see that.Try it: open
/<owner>/<name>/reporton a public repo and read the two lines. Send six reports in a minute from one address to see the 429 on the sixth. The second commit is an empty one the pre-push hook required.Closes #11436