Slice 7: §14 chrome + settings and admin neighborhoods
§14.1 richer landing, §14.2 /philosophy route (disk-backed), §14.3 persistent About link. /settings/notifications surfaces Slice 6's preferences/quiet-hours/mute/watches endpoints. /admin home base consolidates role management, the §6.2 write-mute, the audit-log viewer, the permission-events log, and the §13.2 graduation queue. Backend: backend/app/philosophy.py, backend/app/api_admin.py (seven admin endpoints + user-search), GET /api/users/me/notification-mutes. Frontend: Landing.jsx (deck), Philosophy.jsx, NotificationSettings.jsx, Admin.jsx, App.jsx routing for the chrome surfaces. Tests: backend/tests/test_chrome_vertical.py — 13 cases. Full suite 75/75 green. Spec corrections: §14.2 (PHILOSOPHY.md source is a deployment-time decision), §17 (admin block extended to name the seven new endpoints + user-search and notification-mutes read). §19.1 rewritten for Slice 8 hardening; §19.2 grew four candidates (owner succession, mute-from-actor, the "Following since <date>" disclosure, audit-log row prose). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+132
-41
@@ -29,10 +29,18 @@ rare and surgical and live in the appropriate numbered section per
|
||||
6. **Notifications per §15.** Last, because every other surface
|
||||
produces signals the inbox receives — notification correctness
|
||||
depends on the producers being in place first.
|
||||
7. **The §14 chrome.** Landing page polish, the `/philosophy` route,
|
||||
the persistent About link.
|
||||
7. **The §14 chrome + the settings and admin neighborhoods.**
|
||||
Landing page polish, the `/philosophy` route, the persistent
|
||||
About link; the `/settings/notifications` surface that exposes
|
||||
Slice 6's preferences/quiet-hours/mute/watches endpoints; the
|
||||
`/admin` home base that consolidates role management, the §6.2
|
||||
write-mute, the audit-log viewer, and the §13.2 graduation-
|
||||
readiness queue.
|
||||
8. **Hardening.** End-to-end tests, dev/prod deployment shape,
|
||||
the §12 30/90 branch-hygiene timers.
|
||||
the §12 30/90 branch-hygiene timers, the §19.2 candidates that
|
||||
cluster with deployment (branch-name path routing, cache
|
||||
bootstrap from a pre-existing meta repo, in-app metadata-PR
|
||||
merges, graduation rollback's branch cleanup).
|
||||
|
||||
## State of the codebase
|
||||
|
||||
@@ -280,6 +288,80 @@ adds the `email_opt_out_all` column to `users` for the bounce
|
||||
webhook. Topic 13 settled the rest of the §5 surface before the
|
||||
build started, so no further migrations were needed.
|
||||
|
||||
### Slice 7 — shipped
|
||||
|
||||
The §14 chrome, the §15 settings neighborhood, and the §6/§17 admin
|
||||
home base — three surfaces over existing infrastructure.
|
||||
|
||||
§14.1's pre-login landing carries the title, the subtitle, the
|
||||
short-form pitch from [`PHILOSOPHY.md`](../PHILOSOPHY.md), the
|
||||
sign-in affordance, and a three-item deck that names what the
|
||||
framework is. §14.2's `/philosophy` route reads `PHILOSOPHY.md`
|
||||
through [`backend/app/philosophy.py`](../backend/app/philosophy.py)
|
||||
(disk-backed, configurable via `PHILOSOPHY_PATH`; defaults to the
|
||||
file at the project root) and renders with `marked`. §14.3's
|
||||
persistent About link sits in the header alongside Settings (open
|
||||
to everyone) and Admin (owner/admin only); the chrome's visual
|
||||
budget stays tight per §14.4.
|
||||
|
||||
The notification-settings surface lives at `/settings/notifications`
|
||||
([`NotificationSettings.jsx`](../frontend/src/components/NotificationSettings.jsx))
|
||||
and is what the §15.4 email footer's `Manage all preferences` link
|
||||
resolves to. Five sub-sections: the §15.4 per-category toggles
|
||||
(with the `email_watched_churn` toggle permanently disabled and the
|
||||
§15.4 refusal tooltip inline — naming the refusal is what keeps the
|
||||
contract honest), the §15.5 digest cadence dropdown, the §15.8
|
||||
quiet-hours editor (three inputs against
|
||||
`Intl.supportedValuesOf('timeZone')` with all-three-or-clear
|
||||
validation server-side), the §15.6 watches overview, and the
|
||||
§15.8 per-user mute list with a typeahead add. Owners and admins
|
||||
see the "cannot mute" prose inline per §15.8.
|
||||
|
||||
The admin home base lives at `/admin`
|
||||
([`Admin.jsx`](../frontend/src/components/Admin.jsx)) as a four-
|
||||
tab left-rail: Users (role management + §6.2 write-mute), Graduation
|
||||
queue (§13.2-ready partition), Audit log (paged `actions` with
|
||||
filter chips), and Permission events (paged `permission_events`).
|
||||
Role-grant constraints land server-side in
|
||||
[`backend/app/api_admin.py`](../backend/app/api_admin.py) per §6.1
|
||||
— only owners may grant `owner`; owners cannot self-demote on the
|
||||
role endpoint (the explicit succession path is a §19.2 candidate).
|
||||
The §6.2 write-mute applies only to contributors; admins and owners
|
||||
are not write-mutable. Every role and mute change writes a
|
||||
`permission_events` row joined to `users` for the surface.
|
||||
|
||||
| Method | Path | § |
|
||||
| ------ | --------------------------------------------- | ------- |
|
||||
| GET | `/api/philosophy` | §14.2 |
|
||||
| GET | `/api/admin/users` | §6.1 |
|
||||
| POST | `/api/admin/users/{id}/role` | §6.1 |
|
||||
| POST | `/api/admin/users/{id}/mute` | §6.2 |
|
||||
| GET | `/api/admin/audit` | §6.5 |
|
||||
| GET | `/api/admin/permission-events` | §6.5 |
|
||||
| GET | `/api/admin/graduation-queue` | §13.2 |
|
||||
| GET | `/api/users/me/notification-mutes` | §15.8 |
|
||||
| GET | `/api/users/search` | §15.8 |
|
||||
|
||||
Slice 7 ships covered by
|
||||
[`backend/tests/test_chrome_vertical.py`](../backend/tests/test_chrome_vertical.py) —
|
||||
thirteen integration tests against the FakeGitea, covering the
|
||||
philosophy route for anonymous and authenticated callers, the
|
||||
§15.4 / §15.5 / §15.8 preferences round-trip (including the
|
||||
permanent `email_watched_churn` refusal), the quiet-hours
|
||||
all-or-nothing validation, the §15.8 mute add/list/unmute
|
||||
round-trip, the user-search typeahead, the admin role and
|
||||
write-mute round-trips with their `permission_events` audit, the
|
||||
§6.1 refusal of owner-grant by non-owners, the audit-log filter
|
||||
chips, the graduation-queue partition under both preconditions,
|
||||
and the permission-events listing. The full Slices 1–7 test suite
|
||||
is 75/75 green.
|
||||
|
||||
No schema migrations. The two surface-facing spec corrections —
|
||||
§14.2 (PHILOSOPHY.md source is a deployment-time decision; v1 reads
|
||||
from the app repo) and the §17 admin block (extended to name the
|
||||
seven new endpoints) — are the only places the slice's running
|
||||
code asked the spec to be more honest than it was.
|
||||
|
||||
### Slice 5 — shipped
|
||||
|
||||
Graduation per §13 in full. The §13.3 five-step transactional sequence
|
||||
@@ -510,52 +592,61 @@ spec:
|
||||
|
||||
## Next slice
|
||||
|
||||
**Slice 7: the §14 chrome.**
|
||||
**Slice 8: hardening — the last slice of the v1 build.**
|
||||
|
||||
With Slice 6 shipped, every structural and notification beat the
|
||||
framework commits to is live: propose, claim, super-draft body
|
||||
editing, the §10 PR flow against both repo shapes, graduation, and
|
||||
the §15 inbox/email/digest stack. What remains for v1 is the chrome
|
||||
that wraps the whole thing — the landing page that brings an
|
||||
unauthenticated visitor in, the `/philosophy` route that surfaces
|
||||
[`PHILOSOPHY.md`](../PHILOSOPHY.md) verbatim, the persistent About
|
||||
link in the header per §14.3, plus the natural neighbors that
|
||||
Slice 6 left as API-only and that §19.2 names as candidates:
|
||||
With Slice 7 shipped, every structural beat the spec commits to is
|
||||
live and every surface the framework exposes has chrome around it.
|
||||
What remains is the hardening pass that lets a single-operator
|
||||
deployment actually run end-to-end without hand-holding. Three
|
||||
pieces hang together:
|
||||
|
||||
- **The notification-settings surface** — the actual UI for the
|
||||
preferences/quiet-hours/mute endpoints Slice 6 wired. Topic 13
|
||||
settled the schema and the per-category rules; the surface
|
||||
where a contributor finds the per-category email toggles, the
|
||||
digest cadence dropdown, the quiet-hours editor, the watches
|
||||
overview, and the per-user mute list is the natural follow-on.
|
||||
Likely lives at `/settings/notifications` (the link Slice 6's
|
||||
emails already point at).
|
||||
- **The admin neighborhood.** §19.2's "Admin surfaces" candidate.
|
||||
Role management, the §6.2 app-wide write-mute, the audit-log
|
||||
viewer, the graduation-readiness queue. Topics 12 and 13 both
|
||||
expanded the admin's repertoire without giving it a centralized
|
||||
home; Slice 7 picks the framing.
|
||||
- **Landing page polish.** Slice 1 stood up a minimal landing for
|
||||
the unauthenticated path; §14 commits a richer shape — what the
|
||||
framework is, why it exists, what the visitor's first read should
|
||||
be, and the sign-in affordance.
|
||||
- **The `/philosophy` route.** [`PHILOSOPHY.md`](../PHILOSOPHY.md)
|
||||
rendered inline, reachable from the header on every page, so the
|
||||
reader can return to the framing without leaving the app.
|
||||
- **The §12 30/90 branch-hygiene timers.** §11.5 names the branch
|
||||
lifecycle (open → merged → 30d read-only → 90d deleted-by-bot,
|
||||
with the per-user message-cursor preservation contract); §12
|
||||
formalizes the policy. The wiring is a scheduled task next to
|
||||
the existing `DigestScheduler` — same `run_tick` test-seam shape.
|
||||
The §10.7 90-day deletion timer Slice 3 left explicitly deferred
|
||||
lives here too. Touches `cache.Reconciler` (the natural place to
|
||||
fire the hygiene sweep), `bot.delete_branch` (the §12 actuator,
|
||||
not yet exercised), and the §19.2 cache-bootstrap topic if the
|
||||
hygiene sweep also rebuilds branch state from Gitea after a
|
||||
cache wipe.
|
||||
- **An end-to-end smoke pass** over the working surfaces. Propose
|
||||
→ super-draft → branch → PR → merge → graduate → active-RFC PR
|
||||
→ notification → inbox → email — one or two `test_e2e_smoke.py`
|
||||
cases that exercise the seams a per-slice test wouldn't. Plus
|
||||
the §19.2 follow-ons the hardening pass is the natural place to
|
||||
fold in: branch-name path routing (`{branch:path}` everywhere
|
||||
with route-ordering discipline), cache bootstrap from a
|
||||
pre-existing meta repo (the audit-log-first attribution shape
|
||||
exercised against history the bot did not author), in-app merge
|
||||
for metadata PRs, the graduation rollback's branch cleanup, and
|
||||
the small Slice-2-onward follow-ons that are deferred until the
|
||||
hardening pass demands them.
|
||||
- **The dev/prod deployment shape.** `deploy/` already carries an
|
||||
nginx vhost, a systemd unit, and a runbook stub. Slice 8 proves
|
||||
the bring-up against a fresh host, settles the secret-material
|
||||
handling (the existing `.env.example` plus the §15.4 SMTP
|
||||
wiring), wires the §6 / §15.4 SMTP credentials, and lands the
|
||||
README updates that take a new operator from `git clone` to a
|
||||
signed-in browser.
|
||||
|
||||
What Slice 7 does NOT own:
|
||||
What Slice 8 does NOT own:
|
||||
|
||||
- The §12 30/90 branch-hygiene timers (still Slice 8).
|
||||
- New surfaces. The v1 surface is complete; the hardening pass is
|
||||
about making what's there resilient, observable, and operable.
|
||||
- The §16 deferred items.
|
||||
- New §15 capabilities — Slice 6 shipped the surface; settings UI
|
||||
is exposure of what's already there, not new behavior.
|
||||
- The §19.2 candidate set as a whole — the hardening pass folds in
|
||||
the candidates that naturally cluster with hygiene timers, cache
|
||||
rebuild, and deployment; the rest stay queued for post-v1
|
||||
sessions.
|
||||
|
||||
The carryovers Slice 7 inherits — the existing §14 spec text, the
|
||||
§17 endpoint set including Slice 6's settings endpoints, and the
|
||||
React Router layout already in place.
|
||||
The carryovers Slice 8 inherits — the full §11.5 / §12 spec text,
|
||||
the existing `cache.Reconciler` and `DigestScheduler` shape, the
|
||||
deploy/ infrastructure, and the 75/75 green test suite.
|
||||
|
||||
The next build session should read `SPEC.md`, `README.md`,
|
||||
`docs/DEV.md`, and `SPEC.md`'s §19.1 and pick up Slice 7 cleanly
|
||||
`docs/DEV.md`, and `SPEC.md`'s §19.1 and pick up Slice 8 cleanly
|
||||
without re-briefing. The working agreement in §19.3 continues to
|
||||
apply: implement the slice, correct the spec only where running
|
||||
code reveals it was wrong at a structural level, accumulate new
|
||||
|
||||
Reference in New Issue
Block a user