Commit Graph

24 Commits

Author SHA1 Message Date
Ben Stull 49e91f8829 Contribute rewrite Phase 6: narrow-viewport right-panel drawer
Below 1280px the right-panel (chat + change-card panel) collapses into
an off-canvas drawer toggled from a Chat button in the editor toolbar.
Above 1280px the inline three-column layout is unchanged. Replaces the
Phase 2 narrow-viewport-banner stopgap.

- drawerOpen useState in RFCView; per-session, default closed; tied to
  a .rfc-body.drawer-open class plus .right-panel.drawer-open and the
  backdrop's data-open attribute.
- ChatDrawerToggleButton in each editor-toolbar branch (Contribute,
  Discuss-with-accepted-changes, and a narrow-only fallback toolbar
  for the default state). Badge mirrors change-panel-header: pending
  AI changes + the buffered manual card. Hidden above 1280px via CSS.
- .right-panel becomes position: fixed; top: 48px; right: 0; width:
  min(420px, 92vw); transform: translateX(100%) by default; slides in
  with .drawer-open. Backdrop sibling with opacity transition handles
  click-to-close.
- Escape closes the drawer when open (useEffect listener gated on
  drawerOpen, so it's a no-op when closed).
- Toolbar gets padding-right shifted to drawer-width when the drawer
  is open at narrow widths so the toggle stays clickable to close
  (otherwise it'd sit under the open drawer).
- @media (min-width: 1280px) restores .right-panel to inline flex
  (360px, position: static), hides the backdrop, hides the toggle
  button, and hides the narrow-only toolbar fallback.
- Removes the .narrow-viewport-banner JSX + its CSS block + its
  @media display:none rule.

SPEC.md §8.1 gains a paragraph noting the narrow-viewport drawer
collapse — explicit acknowledgment that the three-column shape adapts
below ~1280px without re-architecting around responsive primitives.

Verified in Vite preview sandbox at 1024px (drawer offscreen,
backdrop opacity 0, toggle visible) and 1440px (inline three-column,
drawer fixed-positioning reset, toggle hidden, backdrop display:none).
Backend was down this session (proxy 502 on /api/me) so the cumulative
Phase 2–5 end-to-end verification gap is still open; sandbox proof
only, as called out in the phase prompt.

Backend integration suite: 125 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:02:52 -07:00
Ben Stull 3212dc19ee Capture OHM cleanup script and Claude dev config
scripts/cleanup-open-human-model.sh is the one-off used on
2026-05-25 to hard-delete the exploratory open-human-model RFC from
wiggleverse/meta, reserving the RFC #1 slot for the eventual `human`
RFC. Kept in-tree as the audit artifact for that removal.

.claude/launch.json captures the frontend dev-server invocation
(port 5180, --strictPort) so contributors and tooling can launch
the same way. .gitignore now excludes .claude/settings.local.json,
which is per-machine permissions and not appropriate to share.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:52:41 -07:00
Ben Stull f1d126e991 Rename landing page to Open Human Model with Wiggleverse RFC attribution
The 5dbcac8 reframe established OHM as the corpus and Wiggleverse RFC
as the substrate. The landing page and the global header still read
"Wiggleverse RFCs," which puts the substrate's name in front of a
visitor when what they're actually visiting is OHM. Flip both surfaces.

- App header brand and Landing's H1 → "Open Human Model"
- Subtitle becomes "An open dictionary of the words humans and
  machines need to agree on" — names the corpus, not the process tool
- Pitch paragraph reworded to lead with "the Open Human Model is the
  corpus this framework produces," with first RFC defining "human"
- New .landing-attribution block at the foot of the deck: "OHM is run
  by Ben on Wiggleverse RFC — the open-source standardization-process
  software that powers it. Anyone can run their own collaboration on
  the same substrate." Reader can now tell instance-vs-substrate at
  a glance
2026-05-25 11:37:27 -07:00
Ben Stull 4afb018bb0 Contribute rewrite Phase 5: inline word-diff in manual pending card
The §8.11 manual-edit card was a stub — "{N} paragraphs edited
directly" plus a countdown and a Save-now button. The spec says the
card should grow one inline word-diff per touched paragraph as the
contributor types, in the same green/red register the Phase 3 accepted
overlay uses. Phase 5 lands that.

In RFCView.jsx, the 800ms-debounced handleEditorUpdate now computes a
per-paragraph token diff alongside the existing paragraph count and
stores both on manualPending. Baseline is the same Phase 4 gutter
source — originalSourceLinesRef.current, the last server-confirmed
body split on blank lines — so the card resets to empty the same
moment the gutter clears (accept/decline/manualFlush/branch-switch).

In ChangePanel.jsx, diffWords is exported (it was already the AI
card's inline-diff engine — token-level LCS over a whitespace-
preserving /(\\s+)/ split, ~30 lines, no runtime dep). The manual
card consumes the same tokens. Wholly-inserted paragraphs render as
all-insert blocks; wholly-deleted paragraphs as all-delete blocks.

Visual register is intentionally shared with Phase 3's preview
overlay: same #dcfce7/#166534 inserts, same #fee2e2/#991b1b
strike-through deletes. Selectors are scoped under .change-manual-diff
rather than reusing .markdown-preview .tracked-* since the card lives
outside the preview surface.

Pre-fancy stance, matching Phase 4's gutter: the diff is index-aligned
against the baseline, so adding a paragraph in the middle lights up
the rest of the doc. Tolerated as the "you've touched stuff below this
point" cue. An LCS-anchored future pass can fix it.

Verification gap matching Phases 2/3/4: backend was not running this
session, so the live RFCView → real-branch flow wasn't exercised.
Drove the Vite preview sandbox instead — mounted ChangePanel with a
hand-built diffs payload, confirmed three blocks render (mixed edit,
wholly-inserted, wholly-deleted), inserts/deletes carry the expected
computed colors, no console errors. Backend integration suite still
green (125 passed).
2026-05-25 11:29:03 -07:00
Ben Stull 886bbf5512 Contribute rewrite Phase 4: §8.10 gutter accent in CM6 raw pane
Restores the §8.10 paragraph-margin marker layer Phase 1 dropped when
Tiptap left, this time against the CM6 raw pane on the left half of
the Contribute split. The matching inline tracked-insert/tracked-
delete overlay Phase 3 shipped lives on the preview pane to the
right; the two visual layers answer different questions on the two
halves of the split — "did anything change in this region?" (gutter,
amber, scannable) vs. "what changed here?" (inline, green/red,
precise) — and are deliberately separate signals.

New file: frontend/src/components/diffGutterExtension.js. The
extension exposes a `setBaseline` StateEffect and a gutter that marks
every line whose text differs from the same-indexed line of the
baseline (the last server-confirmed branch body). Per-line, not
paragraph-block — CM6's natural unit; collapsing to paragraph ranges
is more spec-faithful but adds code, and the per-line stance is the
pre-fancy default. A TODO is left for a future paragraph-collapse
pass if the result reads noisy.

MarkdownSourceEditor.jsx changes:
- Install the gutter extension in the editor's extension list.
- Seed the baseline to `initialDoc` at construct time.
- In the existing `initialDoc`-watching effect, dispatch the doc
  replacement AND a `setBaseline` effect in the SAME transaction so
  there's no one-frame window where the new doc reads as "divergent"
  against the old baseline. This carries through every server-
  confirmed branch refresh that RFCView already wires (accept,
  decline, manual flush, branch switch); no RFCView changes needed
  because all four paths already re-push `initialDoc` after pulling
  fresh state.

Design calls per the Phase 4 prompt's open list:
  • Per-line marks, not paragraph-block ranges. Pre-fancy stance.
  • Amber (#f59e0b) thin 3px vertical bar, distinct from the
    preview pane's green-on-light / red-on-light tracked-change
    inline overlay. Reads as "in-flight / not yet on the server."
  • Baseline reset on every branchView refresh (accept / decline /
    manual flush / branch switch), matching RFCView's existing
    originalSourceLinesRef discipline. Gutter then reads as "what's
    in the buffer but not on the server."
  • No hover / no click. The inline overlay already carries the
    click-to-card binding; the gutter is scannable only.

Known caveats kept deliberately:
  • Insert/delete shifts. Adding a line in the middle shifts every
    subsequent line's index and the naive compare lights up
    everything below — tolerated as the honest "you've touched
    stuff below this point" cue. A future LCS-anchored pass could
    fix it; Phase 4 doesn't need to.

SPEC §8.10:
- First paragraph rewritten to name the CM6 raw pane as the gutter's
  home and replace the stale "ProseMirror plugin" wording with the
  CodeMirror gutter framing. Mirrors how Phase 3 named the preview
  pane as the inline overlay's home.
- DiffView retirement paragraph adjusted: gutter and inline overlay
  together (across the split) cover its review affordance, not
  "both layers in the same surface" — the layers are deliberately on
  different surfaces.

Verification:
- Vite preview sandbox eval — standalone CM6 mount, dispatch tests
  across construct (no marks on identity), per-line edit (mark on
  exactly the touched line, confirmed by Y-coordinate matching the
  line-number gutter element), baseline reset clears, atomic
  doc+baseline dispatch leaves zero marks (the RFCView accept-flow
  path), insert-in-middle exhibits the expected cascade, and
  computed-style proof for the amber bar (`#f59e0b`, 3px wide,
  positioned left of the line-numbers gutter at x=21 vs x=24).
- Screenshot captures the bar on the touched line only.
- 125 backend integration tests still green.
- Live RFCView accept → branch refresh → gutter clear flow against a
  real branch was not driven (backend not running this session,
  carrying forward Phase 2/3's verification gap). The sandbox-level
  proof covers the atomic dispatch correctness; the next session
  with a backend up should drive that golden path before piling
  Phase 5 work on top.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:00:45 -07:00
Ben Stull 2a7c099a33 Contribute rewrite Phase 3: tracked-change overlay in MarkdownPreview
Reintroduces the §8.10 inline tracked-change layer Phase 1 dropped when
Tiptap left, this time anchored to the rendered preview rather than a
writable editor. Each accepted change on the branch decorates the
preview DOM with a `<span class="tracked-insert">` at the proposed text
and a `<span class="tracked-delete">` strikethrough for the original;
hover surfaces the same ChangeTooltip DiffView uses (badge + prompt +
quote + reason), now extracted to its own file so both surfaces share
the affordance.

Design calls per the Phase 3 prompt's open list:
  • Contribute pane: default-on. The pane exists for editorial review
    of your own work; the overlay reinforces that.
  • Discuss pane: opt-in via a new "Show tracked changes" toolbar
    toggle on non-main branches. Clean prose stays the default.
  • DiffView's "Review changes" toolbar is untouched — DiffView dies in
    Phase 7 and overloading its toggle now creates surface to unwind.

Pre-fancy stance on the known overlay subtleties:
  • Drift — if `proposed` no longer appears verbatim (later manual
    edit touched it) we skip the decoration rather than mis-anchor.
  • Overlap — decorate in `acted_at` ascending order; later spans win
    on whatever text is still un-wrapped.
  • Mermaid — text nodes inside `.mermaid-block` are skipped; a tracked
    span that intersects diagram source drops silently. Flagged as a
    known limitation rather than worked around.
  • Code — `<pre>`/`<code>` parents skipped too; matching inside
    verbatim code produced noisy false positives in fixtures.

Backend tests: 125 passed. Helper verified via Vite preview sandbox
eval across eight cases (single-paragraph match, distinct-original
strike, no-match skip, mermaid skip, two-change overlap ordering,
declined/pending ignored, whitespace-normalized cross-newline match,
code-block skip), plus computed-style proof for the new
`.markdown-preview .tracked-insert` / `.tracked-delete` rules. The
end-to-end CM6 → accept → overlay flow against a live branch wasn't
exercised (backend not running this session); the simpler unit-level
verification looked clean, but a future session with the backend up
should drive that golden path before Phase 4 piles on top.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:44:09 -07:00
Ben Stull ee6e3491e7 Drop "prototype/carryover" framing now that v1 is shipped
SPEC, DEV docs, and code comments still talked about the codebase as
a rewrite-in-progress against an external prototype. With v1 shipped
the framing reads oddly — it implies code is provisional when it's
the production thing. Recast §18 as "the technical stack," strip
"carryover from the prototype" comments across backend (api.py,
chat.py, providers.py) and frontend (DiffView, PromptBar,
SelectionTooltip, modelStyles), and rework SPEC §1 / §18 to introduce
OHM up front rather than as a follow-on to a prototype reference.

Also:
- RUNBOOK: bump Python prereq to 3.11+ to match the production VM
  (was 3.13).
- Remove IMPLEMENTATION-PROMPT.md — the original implementation brief
  is no longer load-bearing.
- Add deploy/DEPLOY-NEW-SESSION-PROMPT.md as the durable
  deploy-handoff prompt for new sessions.
2026-05-25 10:32:46 -07:00
Ben Stull 7c3b8fc133 Contribute rewrite Phase 2: split-pane preview with mermaid
Add a rendered preview pane and split the Contribute-mode center
column. Discuss mode is now a single rendered preview (no more
read-only Tiptap mount); Contribute mode renders the CM6 raw editor
on the left and a live-updating preview on the right at 50/50, with
the existing chat + change-card panels untouched on the right.

The new MarkdownPreview component renders markdown via marked and
lazy-loads mermaid on the first encounter of a ```mermaid fence in
any rendered doc. Mermaid (~200 KB gzipped, plus dagre/graphlib
subchunks) stays out of the main bundle and is fetched only when a
diagram actually appears in the rendered content. The marked
renderer is scoped via a per-component Marked instance so the
mermaid-fence behavior does not leak to Editor.jsx / DiffView.

XSS hardening on mermaid: securityLevel: 'strict' is set explicitly
in mermaid.initialize. Verified end-to-end via a sandbox eval — a
hostile `<script>window.X=true</script>` payload inside a mermaid
fence is neutralized (no script tags in the rendered SVG, no global
side-effect, diagram still renders with the offending node label
empty).

The §8.12 selection tooltip is now sourced from window.getSelection()
inside the preview surface rather than Tiptap PM positions. The
SelectionTooltip's existing `{text, coords}` contract is unchanged;
coords come from the selection range's bounding rect. Anchor payloads
for flag threads now carry just the quote text — the PM-position
from/to fields that the Tiptap-era code attached are dropped (they
were never meaningful as durable anchors anyway; quote is what §8.12
and §8.13 specify).

Design decisions confirmed before coding (all defaults):
- PromptBar spans both panes at the bottom of the center column; it
  operates on the document, not on either pane individually.
- Start-Contributing is a hard cut — no transition animation. Phase
  6's chat-drawer collapse will redo the layout machinery anyway.
- Mermaid lazy-loads on first ```mermaid fence detected, not on
  Contribute-mode entry. Keeps the gzipped cost off any doc that has
  no diagrams.
- Below 1280px viewport, a narrow-viewport banner surfaces in
  Contribute mode. The drawer collapse that fixes this properly is
  Phase 6.

Mermaid integration is kept loose for the future authoring tool: the
placeholder DOM node carries the source as a data attribute, the
renderer takes (source) → SVG via mermaid.render, and per-block
memoization keys on source. A future authoring pane can intercept
the placeholder before SVG render without restructuring.

SPEC §8.3 updated to reflect the split-pane Contribute layout — the
smallest edit that captures the new shape.

Verification notes:
- Vite preview on :5180 — sandbox mount of MarkdownPreview confirms
  rendering, mermaid SVG output, securityLevel:'strict' XSS
  neutralization, and the window.getSelection → {text, coords}
  bridge end to end.
- Network log confirms mermaid + sub-chunks are absent from initial
  load and fetched only after first ```mermaid encounter.
- Backend was not running this session, so the manual-debounce
  save-now / accept / decline / live-preview-mirror pathways were
  not driven against a real branch; they remain verified by
  inspection. Phase 1's verification gap therefore carries forward.
- 125 backend integration tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:24:19 -07:00
Ben Stull 5dbcac8906 docs: name OHM as the corpus, "Human" as the first RFC
Reframe the Open Human Model in SPEC.md and PHILOSOPHY.md from "the
first RFC the framework will produce" to "the corpus of RFCs the
framework produces, of which the first defines *human*." Earlier
phrasing collapsed the project (OHM) and the first entry into one
name; this teases them apart.

Also surface the OpenXML APIs / UX downstream-consumer point: OHM is
English-first by design — the markdown bodies are canonical, and the
structured artifacts downstream systems need to actually let humans
and machines interact are derived from that English source, not
authored alongside it. This is part of why markdown round-trip
fidelity matters structurally (cf. the Phase 1 CM6 swap).

Updates the obvious example renames — slug `open-human-model` →
`human`, title "Open Human Model" → "Human", PR-list / breadcrumb /
notification examples — so the SPEC's worked-example consistently
shows OHM-as-corpus with Human as a member. Test fixtures and the
README seed-script invocation still carry the old slug; those are
left for a separate pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:06:30 -07:00
Ben Stull 13d59b5d26 Contribute rewrite Phase 1: CM6 markdown source editor
Swap the Contribute-mode editing surface from Tiptap WYSIWYG to a
CodeMirror 6 markdown source editor. Discuss mode and any read-only
viewing continues to render through Tiptap.

The §8.11 manual-edit debounce now reads the raw markdown source via
CodeMirror's doc, eliminating the lossy `editor.getText()` round-trip
that RFCView.jsx flagged as a §19.2 candidate; what the contributor
typed is exactly what gets POSTed to manual flush.

The §8.10 paragraph-margin gutter accent on Tiptap is dropped — it was
dead in read-only Discuss anyway, and Phase 4 of the Contribute
rewrite adds a change-anchored gutter against the CM6 raw pane.

The Tiptap-side accept-time injection of <span class="tracked-*">
markup also drops out — CM6 can't render those spans, and Phase 3's
preview pane is the proper home for tracked changes. The reviewMode /
DiffView toggle still works against marked-rendered HTML in the
interim until Phase 7 retires it.

Notes:
- Bundle gzipped grows ~50KB for CM6 modules (state/view/commands/
  language/lang-markdown). Mermaid in Phase 2 will be the larger lift
  and should lazy-load.
- Verified the CM6 editor mounts cleanly via Vite dev preview: ref
  handle (`view/getDoc/setDoc`) is wired, `onUpdate` fires on doc
  changes, gutter / line-numbers / active-line all render, and the
  source round-trips verbatim. Could not drive the full RFCView
  Contribute flow end-to-end without a running backend; the manual
  countdown + save-now + accept/decline pathways are verified by
  inspection only.
- 125 backend integration tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 09:49:17 -07:00
Ben Stull 55a8be051a Post-v1: per-RFC credential delegation (funder role) folded into §6.7
Second §19.2 settlement after v1. New §6.7 alongside §6.6: optional
`funder:` frontmatter field names a single gitea_login; a
`funder_consents` app-db row records funder-side opt-in; both halves
required for the binding to activate (two-key rule). Funder universe
replaces — does not augment — the operator universe per-RFC for
attribution-clean resolution. Funder role grants zero §6.1/§6.3
authority. Three revocation paths each restore the operator-credentials
status quo.

§19.2's credential-delegation entry is split: lighter half marked
settled with a pointer to §6.7; operational-realities half (mid-call
failure, rotation, billing, rate-limit attribution) lives on as its
own entry. Test suite is 125/125 green (106 prior + 19 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 06:08:43 -07:00
Ben Stull a255429e57 Post-v1: per-RFC model availability (UX half) folded into §6.6
First §19.2 candidate settled after v1. The heavier per-RFC-model
topic subdivided into UX (this) and credential delegation + funder
role (still §19.2). New §6.6 carries the rule: an optional `models:`
frontmatter field on the meta-repo RFC entry; absent inherits the
operator universe, populated narrows the picker to the intersection
with provisioned providers, `[]` opts the RFC out of AI entirely.
The first resolved entry is the RFC default. §18's ENABLED_MODELS is
reframed as the operator universe.

Code: migration 009 adds nullable cached_rfcs.models_json (NULL ≠ []
is load-bearing); entry.py grows the optional field with absent-vs-
empty round-tripping in parse/serialize; new models_resolver module
holds the rule; api_branches replaces /api/models with the slug-aware
/api/rfcs/{slug}/models and threads the chat + reask paths through
the resolver; api_prs §10.2 uses the resolver and extends the stub
fallback to the opt-out case; frontend passes slug to listModels.

Tests 106/106 green (96 prior + 10 in test_per_rfc_models.py). No
behavioral change for entries without `models:` — operator universe
preserved as default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 05:42:15 -07:00
Ben Stull 36635049c7 Slice 8: v1 ships — integration coverage, runbook, spec corrections
- Five new integration test files raise the suite from 75 to 96 green:
  test_hygiene_vertical (7), test_branch_path_routing (4),
  test_metadata_pr_merge (3), test_cache_bootstrap (4), test_e2e_smoke
  (3). The smoke test walks propose → super-draft → edit branch →
  body-edit PR → graduate → active-RFC PR → merge → notification →
  hygiene-sweep deletion end-to-end.
- deploy/RUNBOOK.md replaces the prior DEPLOY.md stub as a real
  runbook: prerequisites, first-time bring-up, day-2 ops (logs, DB
  backup, secret rotation, the §12 hygiene cadence), rollback shape,
  troubleshooting table.
- backend/.env.example grows the SMTP block, HYGIENE_TICK_SECONDS,
  and WEBHOOK_EMAIL_BOUNCE_SECRET with inline commentary.
- README points to RUNBOOK.md; the "what the build lets you do"
  section adds Slices 7 and 8.
- docs/DEV.md gets a Slice 8 — shipped section; the "Next slice"
  footer becomes the v1-complete epitaph.
- SPEC corrections per the §19.3 working agreement: §10.7 names the
  shared §12 sweep; §12 names the bot as actuator and the per-user
  branch_chat_seen preservation contract; §19.1 marks v1 complete
  and records Slice 8; the five §19.2 candidates Slice 8 folded in
  are marked settled with pointers at the resolution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 04:14:50 -07:00
Ben Stull 1a0c4428af Slice 8 WIP: §12 hygiene + §10.7 + routing + rollback cleanup
- Add §12 30/90 hygiene scheduler in hygiene.py, mirroring the
  DigestScheduler shape; wires next to digest in main.py with the
  same start/stop/run_tick test seam.
- Extend bot.delete_branch to accept actor=None for system gestures,
  per §15.9 (actor_user_id=NULL, on_behalf_of=bot_login).
- Convert every branches/{branch} route in api_branches.py and
  api_prs.py to {branch:path}; move the bare GET to the bottom of
  the router so deeper GETs match before greedy-path swallow.
- Extend api_prs.py's _require_pr to accept pr_kind='meta_metadata'
  so the §9.5 metadata-pane PRs land an in-app merge.
- Graduation rollback now deletes the graduate-<slug>-<6hex> branch
  after closing the PR — §19.2 candidate that lands here.
- Email-bounce webhook gains a WEBHOOK_EMAIL_BOUNCE_SECRET seam.
- FakeGitea grows a DELETE /branches/{branch:path} handler and a
  slashed-branch read; integration tests for the hygiene vertical
  cover the 30d close, 90d delete, post-merge delete, pinned
  exemption, per-user cursor preservation, no-notification rule,
  and the graduation-rollback cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 04:03:09 -07:00
Ben Stull 060fa408a2 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>
2026-05-24 23:40:49 -07:00
Ben Stull f67d0aa0db Slice 6: notifications per §15
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:09:04 -07:00
Ben Stull 1b0968a9a2 Slice 5: graduation per §13
The §13.3 transactional sequence flips a super-draft to active —
five steps with paired undoes, an in-process orchestrator fed by
an asyncio.Queue, the §17 SSE endpoint streaming step transitions
to the dialog. Each step is a new bot primitive that logs an
`actions` row, bracketed by `graduate_start` / `graduate_complete`
for the linkable audit sequence. Rollback runs the undoes in
reverse from the last completed step; merge_pr has no undo by
design per §13.5.

The §9.8 precondition gate is enforced server-side at the top of
POST /graduate so the §13.3 rollback complexity does not grow.
The §13.4 chat migration is a database semantic no-op — the
(slug, branch_name='main') threads keep their identity, only the
interpretation changes. The §9.8 pre-graduation history surfaces
via a new _is_meta_target(rfc, branch) dispatch helper and lands
as pre_graduation_history on /main.

§13.1 claim flow landed alongside since it's the prerequisite for
non-admin graduation — bot.open_claim_pr plus broadening
api_prs._require_pr to accept meta_claim.

45/45 tests green; ten new integration tests cover the validator,
the §9.8 precondition refusal, happy path with audit verification,
mid-sequence rollback at steps 2 and 3, concurrent refusal,
chat-survives-without-data-movement, pre-graduation history, and
the §13.1 claim PR cycle.

SPEC.md §19.1 rewritten for Slice 6 (notifications); §19.2 grew
four candidates surfaced during the slice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 21:52:29 -07:00
Ben Stull 4565a6cb95 Slice 4: super-draft body editing per §9.5 + §9.6
The §17 routing-collapse rule lands in api_branches.py and
api_prs.py — every branches/<branch>/... and prs/<n>/... route
dispatches on the entry's state to pick the right Gitea repo, and
the body extracted from the entry's frontmatter envelope is what
the editor and the diff see. The bot grows open_metadata_pr;
cache grows refresh_meta_branches. Two §17 routes added:
start-edit-branch and metadata. The §9.4 super-draft view replaces
RFCView.jsx's Slice 2 placeholder; a metadata pane modal opens
from the breadcrumb. Branch naming uses edit-<slug>-<6hex> to
dodge the §19.2 path-routing candidate while preserving §9.5's
structural shape.

Covered by tests/test_super_draft_vertical.py (10 tests). The
full Slices 1-4 suite is 35/35 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:43:21 -07:00
Ben Stull a2bf89e90b Slice 3: the PR flow
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:37:54 -07:00
Ben Stull 33d9d7a482 Add deploy/ — nginx vhost, systemd unit, runbook
Single-host deployment of the app at rfc.wiggleverse.org alongside
the existing Gitea instance. nginx reverse-proxies /api/* and
/auth/* to a single uvicorn process on 127.0.0.1:8000 and serves
the Vite build output as static files; certbot adds the TLS cert
in place; systemd supervises the process per §4.2's
single-process-with-WAL-SQLite contract (one worker; raising
--workers would break the invariant).

deploy/DEPLOY.md is the step-by-step runbook covering host prep,
Gitea bot + OAuth setup, .env shape, meta-repo seed, nginx +
certbot, systemd, smoke test, and the update/rollback shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 05:18:28 -07:00
Ben Stull c82328e9ad Add MIT LICENSE
Matches the license on the rfc-app-prototype that this codebase
inherits its §18 carryovers from. PHILOSOPHY.md frames the framework
as public infrastructure; MIT is the lowest-friction license for
that posture.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 05:10:50 -07:00
Ben Stull b57b4ffc4b Add seed_test_rfc.py for §8 manual testing
Slice 5 will own the §13 graduation flow that creates per-RFC repos
from super-drafts; until then, the §8 active-RFC view needs a dev
shortcut to bring an `state: active` entry plus a per-RFC repo into
existence. The script:

- Creates `<org>/rfc-NNNN-<slug>` via bot.ensure_rfc_repo_seed,
  seeding RFC.md on main.
- Registers the §4.1 webhook on the per-RFC repo.
- Creates (or graduates) the meta-repo entry with state=active,
  id=RFC-NNNN, repo=<full>.

Requires the operator's gitea_login to have a `users` row (sign in
once via OAuth first); refuses to synthesize a fake user since the
§6 / §15.9 attribution surfaces read from it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 05:01:20 -07:00
Ben Stull 3bc8fe92af Slice 2: the §8 active-RFC view in full
Per the §19.1 brief: the three-column shape (§8.1) opens on main
in discuss mode (§8.2), supports the §8.3 discuss-vs-contribute
flip on non-main branches, hosts §8.4's per-branch chat with AI
participation (§18's <change> protocol → §8.14 changes rows), the
§8.8 change-card panel with §8.9 accept/decline/edit-before-accept,
the §8.10 tracked-change markup + DiffView toggle, the §8.11
manual-edit flushes with the stale-change mechanic, the §8.12
range and paragraph sub-threads, the §8.13 flag affordance, and
the §8.14 discuss-mode buffer.

Backend: bot.py grew per-RFC-repo write ops (cut_branch_from_main,
commit_accepted_change with the structured original/proposed/reason
body and Change-Id + Source-Message-Id + On-behalf-of trailers,
commit_manual_flush, ensure_rfc_repo_seed). cache.py grew
refresh_rfc_repo and the webhook dispatches on repository.full_name.
providers.py and chat.py port the §18 carryovers — multi-provider
LLM abstraction and SSE-streaming chat against the §5 threads /
thread_messages / changes schema. api_branches.py mounts the §17
branches/<branch>/* and threads/<thread_id>/* routes with the §6
/ §11 permission checks inline.

Frontend: RFCView.jsx rebuilt as the §8 surface; Editor.jsx,
ChatPanel.jsx, ChangePanel.jsx, PromptBar.jsx, SelectionTooltip.jsx,
DiffView.jsx, ModelPicker.jsx, modelStyles.js lifted from the
prototype and adapted to the canonical schema.

Covered by `backend/tests/test_rfc_view_vertical.py` — eleven new
integration tests against an extended FakeGitea (PUT contents,
POST orgs/{org}/repos, seed_rfc_repo): main-view read,
promote-to-branch, accept (with and without edit-before-accept),
decline, manual flush + system message, flag creation, visibility
flip, anonymous read-but-no-contribute, stale-change refusal, and
the chat-streaming path with a fake provider injected. The 5
Slice 1 tests continue to pass alongside.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 04:35:14 -07:00
Ben Stull 779ba6db59 Slice 1: scaffolding + propose-to-super-draft vertical
Brings the §1 bot wrapper, the §4 cache (webhook + reconciler), the
§5 schema (six numbered migrations), Gitea OAuth + §6 user
provisioning, the §7 catalog left pane, and the propose-to-merge
vertical: propose modal opens an idea PR against the meta repo, an
owner merges from the pending-idea view, the cache picks it up via
webhook or reconciler sweep, and the catalog renders the new
super-draft.

Per §1 the bot is the only Git writer; every commit, branch
creation, and PR merge carries the §6.5 On-behalf-of: trailer and
an `actions` audit row. Per §4 the cache is never written from a
user action — it's webhook+reconciler only.

Covered by `backend/tests/test_propose_vertical.py` against an
in-process Gitea simulator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 04:31:11 -07:00