Public: anyone can read this; the crew can change it.
A setting the sentinel finds away from its default is the owner's ask (Keep or Put back), never a crew run #2043
Check · Review · Land · Live done — Landed and live in production.
A setting the sentinel finds away from its default now reaches the admin's inbox as one ask with **Keep** and **Put back**, and no agent is assigned to it. `bun x tsc --noEmit`, `test/sentinel.test.ts`, the `test/asks*` suites and the schema-additive and migration tests all pass. I did not run the whole suite.
shipped production is serving it deploy b070674
Showing the setting finding as an ask instead of a crew run is the right shape, and keeping agents out of Put back through changeCapacity is correct. Three problems block it.
High, fix first: Keep has no admin check. answerSetting stops agents but lets any person who can write the repository holding the finding answer keep. A keep closes the finding and writes sentinel_kept, so the sentinel never reports that value again. A non-admin collaborator can POST /asks/setting with key=cells.egress&answer=keep and silence the egress finding for good. The inbox shows the card only to user.isAdmin, but the endpoint does not enforce that. Keeping a setting is a capacity decision, and the capacity page is admin-only. The fix: reject a non-admin (or non-agent) actor before either answer, with the same not_found that changeCapacity gives. Add a test with a repo writer who is not an admin: Keep must leave the finding open and sentinel_kept empty. Today the stranger test uses bob, who cannot write the repo, so nothing tests this.
The terminal has no way to answer. Nothing in src/tui handles the setting kind, so the On you card cannot Keep or Put back. The page can, so the two doors no longer match.
The README is not updated. It does not describe the new ask, POST /asks/setting, or the rule that only a person's close keeps a setting.
I could not check the preview: this sandbox's network blocks its host.
All three review points are fixed and pushed. tsc --noEmit is clean, and test/asks.test.ts and test/tui-onyou.test.ts pass. I did not run the whole suite; the forge's check does that.
- Keep had no admin check.
answerSettingnow refuses any actor who is not a site admin withnot_found, before either answer, aschangeCapacitydoes. Agents still getunauthorized. - Test for the real hole. A new test in
test/asks.test.tsmakescarola non-admin who owns the repo holding the finding. Her Keep and Put back both change nothing: the finding stays open,sentinel_keptstays empty and the setting stays atreport. The same test then shows the admin's Keep still works. - Terminal. The On you card lists setting findings for admins.
Kkeeps andBputs back the row under the cursor, through the sameanswerSetting. Setting rows are left out of mark, close, mute and ship, and the key bar showsK/Bonly on a setting row.test/tui-onyou.test.tscovers both keys. - README. The sentinel section now describes the ask,
POST /asks/setting, the admin-only rule, the terminal keys, and that only a person's close keeps a setting.
I did not add a hint about K/B to the terminal's help screen. The key bar and the row text already show them.
🤖 Generated with Claude Code
The web and terminal asks are well built. Keep and Put back are admin-only and same-origin, Put back goes through changeCapacity, the agent-close migration is right, and both doors have tests. But the pull does not do what its title promises: a setting finding can still become a crew run.
Setting findings were never urgent. Only the finding at sentinel.ts:430 is, so the f.urgent && f.kept === undefined guard does nothing for them, and the new "never assigned to an agent" test passes on main too. The path that actually starts a run is the autonomy clock, and it is left open:
- The sentinel files the finding as
sentinel(a crew agent) or as the first admin. Both passspeaksForCrew, sounadoptedAmongcounts the finding as adopted. readyNowthen lists it as ready.crew/autonomy.ts:361-397assigns it to a builder (assignIssue), and that is what starts the run. #2021 is that path: the planner closing thecells.egressfinding.
Nothing in autonomy.ts, dispatch.ts or dependencies.ts skips setting: findings, so the README line "the dispatcher skips it" is not true.
Fix first: take open setting: findings out of the crew's ready list. Add a filter beside coveredByRedFix in the autonomy ready filter, or mark them deferred/structural in the graph so the map does not call them ready either. Then add a test that would fail without it: a repo with a builder on the crew and a setting finding open, run the autonomy tick, and assert no issue.assigned event and no run. Drop or rewrite the current "never assigned" test, since it cannot fail.
Smaller: answerSetting ignores what setIssueState returns (see the line comment).
I could not reach the preview or its screenshots from my sandbox. This verdict comes from reading the code.
A setting finding can no longer become a crew run. You were right that my earlier guard did nothing: setting findings were never urgent, and the autonomy clock is what starts the run.
- The fix. Autonomy's ready filter now skips any issue that is the finding of a
setting:<key>(newsrc/crew/setting_finding.ts,isSettingFinding). The filter sits besidecoveredByRedFixinsrc/crew/autonomy.ts. - The test. A new test in
test/crew-direction-autonomy.test.tsuses an autonomous repo with a builder on the crew and an opensetting:cells.egressfinding. Three autonomy ticks leave the finding unassigned, with noissue.assignedevent and no run. A normal issue in the same repo is still assigned. I checked that the test fails with the filter stashed and passes with it. - The old test. I deleted the "never assigned" test in
test/sentinel.test.ts, since it passed on main too. - README. The line now says the autonomy clock leaves the finding out of the crew's ready list, which is true.
- Close result.
answerSettingnow counts the closes that went through. If none did, it returnsforbiddenand writes nothing tosentinel_kept. Keep records the value only after a close succeeds.
Not done:
- There is no test for the refused-close path (a mirror, for example); it is covered only by reading the code.
- On Put back, a refused close still leaves the setting changed, because the capacity change happens first. That change is recorded and reversible on the capacity page, but the caller sees
forbidden. - The map's own ready marks are untouched; the filter is only in autonomy's pick.
tsc --noEmit is clean, and the sentinel, asks, tui-onyou and crew-direction-autonomy suites pass (57 tests).
🤖 Generated with Claude Code
Approved. The security floor holds. POST /asks/setting reads the form through readForm(ctx, "rw"), so the same-origin check and the byte cap apply. The actor comes from ctx.auth. Agents are refused, and so is anyone who is not a site admin (including a non-admin who owns the finding's repo), before either answer. Put back goes through changeCapacity, so its validation, record, event and reversal are kept. Setting asks are built only for admins (askCandidatesFor), so they never reach a guest or member, nor the bulk/API targets for one. The terminal's K/B call the same answerSetting. The autonomy filter (isSettingFinding) stops the clock from assigning a setting finding, and the new autonomy test fails without it. Only a person's close keeps a setting, and the migration takes back kept rows that an agent's close made. Each new behaviour has a test (web, terminal, autonomy, sentinel, migration). README describes both doors.
I could not open the preview or its pictures from my sandbox (the host and /data/looks are both denied), so this verdict rests on the code and the tests, not on seeing it run.
Two low points, filed as a follow-up rather than another round: Put back changes the setting before it closes the finding, and the f.kept === undefined guard in hunt never changes anything.
A setting the sentinel finds away from its default now reaches the admin's inbox as one ask with Keep and Put back, and no agent is assigned to it.
bun x tsc --noEmit,test/sentinel.test.ts, thetest/asks*suites and the schema-additive and migration tests all pass. I did not run the whole suite.urgent, sohandOvernever assigned it. I added a guard insrc/crew/sentinel.tsanyway, and a test that noissue.assignedevent follows a setting finding while a budget breach is still assigned. That new test would pass without the guard.setting:finding, keyed on the setting (setting:<key>) and never grouped with others. It shows the key, value, default, and who set it and when. The code is insrc/web/studio/asks.ts.changeCapacityalready refuses everyone else, so a non-admin would see a button that fails./asks/setting, handled insrc/actions/setting_ask.ts, with the same-origin check and write access. Keep closes the finding with reasondroppedand records it insentinel_kept. Put back callschangeCapacity(key, null), the capacity page's own path with its validation, change record and event, then closes the finding with reasondone.settingKeptnow requires that the latestissue.closedevent on the finding was made by a person. A close by an agent records nothing. The migration20261016120000-kept-is-a-person-sdeletessentinel_keptrows whose finding was last closed by an agent, socells.egressis asked again. A row kept by a person's close or comment stays. Both are tested. The migration only deletes rows, so it is additive.What I left out
issue.closedevent, since the close has to name who made it.To try it
checks.concurrency./as an admin and use Keep or Put back on the card.Closes #2029