Remove the blanket .agent/ gitignore so the phase roadmap, user stories, reports, and PLAN.md are versioned with the code. Only runtime artifacts (.agent/phase-sessions/, .agent/pipeline.log) remain ignored. Update AGENTS.md git protocol rule to match.
4.3 KiB
Phase 27 — Global Tuning Manager
Source: TODO.md L3 — "Add a way to add 'global tuning' without having a chat to reply to. Also previous tunes should be editable."
Story: .agent/user_stories/global-tuning.md
Context: Phase 15 added steering notes (owner instructions injected into every system prompt as the <tuning> section); phase 16 gated the whole /api/steering router behind require_admin. The current UI only lets the admin create a note by pressing "Tune" under a completed chat bubble, and the header "Tuning" panel lists notes newest-first with delete-only per note. There is no way to add a note without a chat, and no way to edit an existing one.
Objective
Give the admin a Global Tuning page (/tuning.html) where notes can be created, edited, listed, and deleted without any chat conversation — and expose it via an admin-only header button. Add a PUT /api/steering/{note_id} endpoint for updates. The existing chat-page "Tune" button and header panel keep working (create + delete) so nothing regresses.
Dependencies
15_steering_notes(complete) — thesteering_notestable, the<tuning>prompt section, the/api/steeringrouter (list/create/delete), and the chat-page "Tune" button + header panel this phase augments.16_admin_auth(complete) —require_admin+ the signed-cookie auth; the tuning page is admin-only, following the same gate pattern as the Sources page.19_shared_header(complete) — the shared header bar that gains the admin-only "Tuning" link.
Tasks
01_steering_put_endpoint.md— addPUT /api/steering/{note_id}(update a note; 404 unknown, 422 invalid) +PATCH-friendly schema inapp/schemas.py02_tuning_page_html_css.md— createfrontend/tuning.html(title, note list, create form, edit-in-place) + CSS03_tuning_js_crud.md— createfrontend/assets/tuning.jswith full CRUD (list, create, edit, cancel, delete) + live announcer04_header_button_and_e2e.md— add the admin-only "Tuning" header button →/tuning.html; E2E suite for the tuning page
Testing & Quality
- Unit/integration:
PUT /api/steering/{note_id}(admin 200, anon 403, unknown 404, invalid 422) — unit + integration. - Coverage: >90% on
app/(new endpoint + schema). - E2E:
tests/e2e/test_global_tuning.py— the story gate, run in isolation.
Completion Criteria
- Admin can open
/tuning.html(via the header button) and create a note without any chat; the note appears in the list and steers future answers (verifiable via the chat page after a turn, or via the list). - Admin can edit an existing note inline (edit button → textarea pre-filled → Save → updated text); the change is reflected in the list and in the
<tuning>prompt (integration test). - Delete still works; create-then-edit-then-delete round-trips cleanly.
- Anonymous users get 403 on
/tuning.html's data and onPUT /api/steering/…; the header "Tuning" link is hidden for anonymous. - The chat-page "Tune" button and header panel are unchanged (create + delete still work).
uv run pytestgreen;uv run pytest --cov=app --cov-report=term-missingTOTAL ≥ pre-change number.uv run pytest tests/e2e/test_global_tuning.py -v --no-covgreen in isolation.uv run ruff check . && uv run pyrightclean.- UI Structure Check (AGENTS.md rule 5): tuning page — landmarks, labeled controls, contrast ≥4.5:1, focus-visible, ≥44px targets, centered column, no CDN.
.agent/user_stories/global-tuning.mdexists.- One
--no-gpg-signcommit staging only this phase's files;.agent/phases/todo/27_global_tuning/moved to.agent/phases/complete/.
Locked decisions
- A10 untouched —
PUT /api/steering/{note_id}is a new stateless route under/api; the API stays stateless, admin-only viarequire_admin(the existing gate). - A11 untouched — vanilla HTML/CSS/JS, no CDN, no new packages, system font stack; the tuning page is a new page + new module script.
- No schema change / no migration — the
steering_notestable already storesnote;PUTupdates the existingnotecolumn (A13 untouched). - A16 honoured — one new story E2E suite + adapted regressions.
- A17 honoured — one atomic
--no-gpg-signcommit.