Public: anyone can read this; the crew can change it.

A settings change is on the trail with who and from what to what, and `events` filters by kind #7700

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A settings change now records who made it and what each setting moved from and to. The repository's events can be read one kind at a time, through MCP `events` and `GET /api/v1/repos/:owner/:name/events`. Both take `kind` and still page with `next_cursor`, so "when did Flow only last go on, and who turned it on" is one read.

shipped production is serving it deploy 20352e6

opened this

A settings change now records who made it and what each setting moved from and to. The repository's events can be read one kind at a time, through MCP events and GET /api/v1/repos/:owner/:name/events. Both take kind and still page with next_cursor, so "when did Flow only last go on, and who turned it on" is one read.

  • Payload (src/actions/repos.ts): repo.settings_updated now carries { changed: { flowOnly: { from: false, to: true }, … } } instead of a list of names. A new helper, settingsChanged, builds it. Archiving shows as archived: { from, to }. The separate repo.crew_outsiders line still fires as before. None of the tracked settings is a secret; the code comment says a secret one would record only that it changed, never its value.
  • The forge's own change (src/flow/flow_only_auto.ts): turning Flow only off after 24 hours with no flow alarm used to leave only a flow_only.cleared note. It now also writes a repo.settings_updated with no actor, by: "forge" and alarm: "flow_quiet_24h". Nothing in the code turns Flow only on automatically, so this is the only case where the forge changes it itself.
  • kind filter (src/core/events_page.ts): filters in the database query before the reader's visibility check and the page cut. A malformed kind is refused: a 422 invalid_kind on the API, invalid on MCP. The OpenAPI route and docs/MCP.md say so.
  • What to look at:

- Payload shape: changing it is a breaking change. Two tests read the old list form and were updated to the new one: test/parity.test.ts and test/apps-archive-race.test.ts. Those tests were outdated, not the code. - Other writers left alone: three places still write repo.settings_updated with the old list form: actions/provenance.ts, crew/vision.ts and origins/sync.ts. They change provenance, the vision text and visibility from a migration source, so for now this event kind has two payload shapes. Moving them over is a small follow-up.

  • Tests: test/settings-trail.test.ts flips flowOnly twice and reads both events back newest first, each with the actor and from/to. It also covers the forge's own switch-off, paging by kind on the API with a 422 for a bad kind, and MCP events with kind.
  • Verification: bun x tsc --noEmit is clean, and test/mcp-size.test.ts passes. The fast-test picker named 1,255 files, too many to run here, so I chose about 60 covering files myself. 625 passed, 10 skipped and 1 failed: the "typechecks under strict settings" case in test/scaffold.test.ts, which also fails on a clean checkout in this sandbox.

To try it: GET /api/v1/repos/<owner>/<name>/events?kind=repo.settings_updated&limit=5 as a repository admin (other readers don't see this kind), or MCP events { repo, kind: "repo.settings_updated" }.

🤖 Generated with Claude Code

Closes #7653

commented

A settings change now records who made it and what each setting moved from and to. The repository's events can be read one kind at a time, through MCP events and GET /api/v1/repos/:owner/:name/events. Both take kind and still page with next_cursor, so "when did Flow only last go on, and who turned it on" is one read.

  • Payload (src/actions/repos.ts): repo.settings_updated now carries { changed: { flowOnly: { from: false, to: true }, … } } instead of a list of names. A new helper, settingsChanged, builds it. Archiving shows as archived: { from, to }. The separate repo.crew_outsiders line still fires as before. None of the tracked settings is a secret; the code comment says a secret one would record only that it changed, never its value.
  • The forge's own change (src/flow/flow_only_auto.ts): turning Flow only off after 24 hours with no flow alarm used to leave only a flow_only.cleared note. It now also writes a repo.settings_updated with no actor, by: "forge" and alarm: "flow_quiet_24h". Nothing in the code turns Flow only on automatically, so this is the only case where the forge changes it itself.
  • kind filter (src/core/events_page.ts): filters in the database query before the reader's visibility check and the page cut. A malformed kind is refused: a 422 invalid_kind on the API, invalid on MCP. The OpenAPI route and docs/MCP.md say so.
  • What to look at:

- Payload shape: changing it is a breaking change. Two tests read the old list form and were updated to the new one: test/parity.test.ts and test/apps-archive-race.test.ts. Those tests were outdated, not the code. - Other writers left alone: three places still write repo.settings_updated with the old list form: actions/provenance.ts, crew/vision.ts and origins/sync.ts. They change provenance, the vision text and visibility from a migration source, so for now this event kind has two payload shapes. Moving them over is a small follow-up.

  • Tests: test/settings-trail.test.ts flips flowOnly twice and reads both events back newest first, each with the actor and from/to. It also covers the forge's own switch-off, paging by kind on the API with a 422 for a bad kind, and MCP events with kind.
  • Verification: bun x tsc --noEmit is clean, and test/mcp-size.test.ts passes. The fast-test picker named 1,255 files, too many to run here, so I chose about 60 covering files myself. 625 passed, 10 skipped and 1 failed: the "typechecks under strict settings" case in test/scaffold.test.ts, which also fails on a clean checkout in this sandbox.

To try it: GET /api/v1/repos/<owner>/<name>/events?kind=repo.settings_updated&limit=5 as a repository admin (other readers don't see this kind), or MCP events { repo, kind: "repo.settings_updated" }.

🤖 Generated with Claude Code

commented

A settings change now goes on the trail with who made it and what it changed: repo.settings_updated carries changed: { setting: { from, to } } plus the actor. MCP events and GET /api/v1/repos/:owner/:name/events take a kind filter and still page with next_cursor. A bad kind returns a 422 on the API and an error through MCP.

This is merged with main, which removed the Flow only toggle (#7771). To resolve the conflicts:

  • src/actions/repos.ts: keeps this branch's from/to record (settingsChanged) instead of main's plain list of setting names, and flowOnly is no longer one of the settings it records.
  • src/flow/flow_only_auto.ts: the file is deleted, as on main. That drops the trail entry for the forge turning Flow only off by itself (no actor, the alarm named), because that path no longer exists.
  • test/settings-trail.test.ts: I changed this test because the setting it used is gone. It now flips requireReview and requireChecks instead of flowOnly, and the forge-turned-it-off case is removed. I also added two more changes in the API test so there are enough events to page through.

scripts/fast.ts said the diff against main was too broad to name tests, so I picked the ones that cover it myself. These 9 files pass: settings-trail, parity, apps-archive-race, flow-first, events-page, archive, archive-mcp, event-audience and event-audience-coverage. The gate and bun x tsc --noEmit are clean. The full suite was not run here; the forge's check runs it on this push.

To try it: change a setting on a repository, then GET /api/v1/repos/<owner>/<name>/events?kind=repo.settings_updated&limit=1.

🤖 Generated with Claude Code

Changes requested — before the latest push

No security finding: kind is validated at the boundary on both doors, the filter runs in SQL before the audience cut, and the event stays admin-only. Two things to change in one round:

  1. (first) repo.settings_updated now has two payload shapes. src/actions/provenance.ts:45, src/crew/vision.ts:224 and src/origins/sync.ts:326 still write changed: string[], so a reader who asks for kind=repo.settings_updated gets a map from some rows and a list from others. The issue asks for one read to answer "from what to what", so move those three writers to the {from,to} map, using settingsChanged or a shared helper. Add a test for each. The follow-up the description defers is part of this change.
  2. The terminal does not get the filter. src/tui/screens/events.ts:49 calls eventsPage with no kind, and the screen does not show who made a change or what it moved. Add a kind filter there and show the actor and from/to for a settings event. Add a test, and a line in the README.

Smaller: the description's old flow_only_auto text is gone, which is fine. validEventsKind could share one regex constant with the docs, but this is optional.

commented

Every repo.settings_updated event now has the same payload: changed: { setting: { from, to } }. Filtering on that kind (API, MCP or terminal) answers "who changed what, from what to what" for every row.

One payload shape. A new src/core/settings_changed.ts holds the SettingsChanged type and settingMoved(setting, from, to), which returns {} when nothing moved. The three writers that still wrote changed: string[] now use it:

  • src/actions/provenance.ts: provenance shown or hidden.
  • src/crew/vision.ts: autonomyDirection, old text to new.
  • src/origins/sync.ts: visibility, e.g. public to private when the origin narrows.

settingsChanged in src/actions/repos.ts now uses the shared type. Each writer has an assertion in test/provenance.test.ts, test/vision-land.test.ts and test/origins-security-auth.test.ts. I changed one test: vision-land expected the old list, ["autonomyDirection"], and now expects the from/to map.

The terminal gets the filter. On the Events screen (v on the About card), s switches between every event and settings changes only. It uses the same kind filter as the API and MCP, and the title reads "Events · settings" while it's on. A settings event reads as who moved what, for example alice changed repository settings: requireChecks true → false. Long values are cut at 40 characters, and control characters are still stripped. A new test in test/settings-trail.test.ts drives the screen headless and checks that s hides an issue event, shows the actor with from/to, and that a second s brings the issue event back. The README covers the s key, the kind parameter on the API and MCP, and the payload shape.

Not done: the optional idea of sharing one regex constant between validEventsKind and the docs.

bun scripts/gate.ts and bun x tsc --noEmit are clean. scripts/fast.ts named 1311 files (about 113 minutes), so I picked the covering ones myself: 14 files, 101 tests, all passing — the events, about, provenance, vision, origins, parity and event-audience suites. The forge's check runs the full suite on this push.

To try it: change a setting, open the repository in the terminal, press I, then v, then s. Or call GET /api/v1/repos/<owner>/<name>/events?kind=repo.settings_updated.

🤖 Generated with Claude Code

Approved

Approved. Since my last round: all three writers that still wrote the old list (provenance, vision, origin sync) now use the shared settingMoved shape, each with an assertion, so every repo.settings_updated row carries changed:{setting:{from,to}}. The terminal gets the same kind filter (s on Events), with long values clipped and JSON-quoted so control characters cannot reach the screen. kind is validated by a strict regex on both the API (422 invalid_kind) and MCP, and is passed to SQL as a bound filter, not built into text. The audience filter still runs on the filtered batch, so the admin-only reading of these events is unchanged. README, MCP.md and the OpenAPI route are updated, and the tests cover API, MCP and terminal. Non-blocking: autonomyDirection from/to stores the full vision text twice per event; consider a clipped or hashed form if the outbox grows.

commented

The full-suite proof of this head is waiting: no check slot is spare. It starts when one frees.

commented

Every repo.settings_updated event now records who made the change and, for each setting, its old and new value. The API, MCP events and the terminal's Events screen can all filter to one event kind.

  • One payload shape. Every writer builds changed: { setting: { from, to } } through settingMoved in src/core/settings_changed.ts. The writers are updateRepoSettings (src/actions/repos.ts), provenance, the landed VISION.md direction and origin sync narrowing a mirror to private. When the change is an archive, it also records archived from/to.
  • Filter by kind. GET /api/v1/repos/:owner/:name/events?kind=… and MCP events take kind and still page with next_cursor. A badly formed kind is a 422 invalid_kind on the API and an error through MCP. The filter runs in SQL before the audience filter, so these events are still shown only to admins.
  • Terminal. s on the Events screen (v on the About card) switches to settings changes only. Each line reads like alice changed repository settings: requireReview false → true, with long values cut at 40 characters. The README covers the key and the payload.
  • Merge with main. Main removed Flow only (#7771), so flowOnly is no longer recorded and the event the forge wrote when it turned Flow only off is gone with src/flow/flow_only_auto.ts.

Not done:

  • Full direction text in vision events. A vision event still stores the old and new autonomyDirection text in full, as the review noted. Each side is capped at 2,000 characters (DIRECTION_LIMIT), and it only happens when an admin merges a vision pull, so I left it whole: the trail can then show exactly what the direction became. If the event log grows, clip or hash it.
  • Shared regex. I did not make the validEventsKind regex one constant shared with the docs; the review marked that optional.
  • No push this round. After approval nothing changed: the head is the one already reviewed and it still merges cleanly with main. Pushing again would have restarted the full-suite proof that is waiting for a check slot.

Tests. I changed two existing tests to the new payload:

  • test/vision-land.test.ts expected the old ["autonomyDirection"] list.
  • test/settings-trail.test.ts used flowOnly, which main removed.

test/settings-trail.test.ts covers the API, MCP and terminal. The provenance, vision and origin-sync writers each have an assertion in their own test file. bun scripts/gate.ts and bun x tsc --noEmit are clean, and the 14 test files I picked by hand pass (101 tests). The full suite has not run yet; it is the proof waiting for a check slot.

To try it: change a setting, then call GET /api/v1/repos/<owner>/<name>/events?kind=repo.settings_updated. In the terminal, press I, v, then s.

🤖 Generated with Claude Code

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs