feat(web-ui): Glitch Capture entry point and form for PROOF9 (#568)#576
feat(web-ui): Glitch Capture entry point and form for PROOF9 (#568)#576
Conversation
Closes #568 Adds the [Capture Glitch] button to the PROOF9 page header alongside [Run Gates], opening a full modal form that calls POST /api/v2/proof/requirements and re-fetches the requirements table on success. Changes: - CaptureGlitchModal component: description (required), source dropdown, scope textarea, 9-gate checkbox list (≥1 required), severity dropdown, optional expiry date; validates before submit, shows inline errors, resets state on reopen - proofApi.capture() method added to api.ts - CaptureGlitchRequest type added to types/index.ts - Exported from proof/index.ts - Wired into proof/page.tsx with captureOpen state + mutate() on success - 12 new tests covering rendering, validation, submission, error, reset - ProofPage.test.tsx mock updated to include CaptureGlitchModal + GateEvidencePanel (were missing, causing 18 pre-existing test failures) Tests: 715/715 pass. Build: clean.
WalkthroughThis pull request introduces a "Capture Glitch" feature to the PROOF9 page, enabling users to convert production failures into permanent proof requirements. It adds a new modal component with a form for collecting failure details, integrates it into the proof page with a button, extends the API client with a Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
Review pending - writing to file first |
Review: Phase 3.5C Glitch Capture Web UISolid implementation overall. Three bugs to fix before merge. Bug (Medium): Gate checkboxes are never sent to the backend
The backend Two options:
Validating but silently discarding is the worst outcome. Bug (Low): Expiry date field is dead codeThe Bug (Low): Axios error detail extraction always returns undefinedIn const detail = (err as { detail?: string })?.detail;FastAPI error responses land at const axiosErr = err as { response?: { data?: { detail?: string } } };
const detail = axiosErr?.response?.data?.detail;
setError(typeof detail === 'string' ? detail : 'Failed to capture glitch. Please try again.');Minor: Redundant aria-label alongside label htmlForElements already associated via Test coverage noteOnce gate/expiry is resolved, update the submission test to verify exactly what is sent rather than relying on What is working well
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
web-ui/src/__tests__/components/proof/CaptureGlitchModal.test.tsx (1)
125-134: Strengthen payload assertions to cover all mapped form fields.The submit test currently won’t catch regressions for
gates/expiresmapping. Add them to the expected payload once serialization is in place.Suggested assertion update
expect(mockCapture).toHaveBeenCalledWith( WORKSPACE, expect.objectContaining({ title: 'Something broke in production', description: 'Something broke in production', + where: 'production', severity: 'high', source: 'production', created_by: 'human', + gates: ['unit'], + expires: null, }) );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/src/__tests__/components/proof/CaptureGlitchModal.test.tsx` around lines 125 - 134, The test assertion for CaptureGlitchModal's submit should also assert the serialized gating and expiry fields: update the expect(mockCapture).toHaveBeenCalledWith(WORKSPACE, expect.objectContaining({...})) payload to include gates (e.g., gates: <expected serialized value>) and expires (e.g., expires: <expected serialized value>) alongside title, description, severity, source, and created_by; locate the assertion in CaptureGlitchModal.test.tsx and add the gates and expires keys matching how the component serializes those form fields so regressions in gates/expires mapping are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web-ui/src/components/proof/CaptureGlitchModal.tsx`:
- Around line 87-115: The submit handler (handleSubmit) currently builds
CaptureGlitchRequest without including selectedGates and expires, so those
validated inputs are dropped; update handleSubmit to add selectedGates (map to
an appropriate field name like gates or affected_gates) and expires (map to
expires_at or ttl) into the body object, update the CaptureGlitchRequest type
definition to include these fields, and ensure the backend request model/handler
accepts and maps those new fields as well so user input is preserved end-to-end.
- Line 130: The modal in CaptureGlitchModal uses DialogContent with class
"max-w-lg", producing a centered dialog instead of a full-screen slide-over;
update the DialogContent usage in CaptureGlitchModal to remove "max-w-lg" and
replace it with full-screen/slide-over appropriate utility classes (e.g., full
width/height and no max-width) so the component renders edge-to-edge on small
and large screens, and adjust padding/margin classes as needed to preserve inner
layout and accessibility (keep the same component name CaptureGlitchModal and
DialogContent so you only change the className and related layout utilities).
---
Nitpick comments:
In `@web-ui/src/__tests__/components/proof/CaptureGlitchModal.test.tsx`:
- Around line 125-134: The test assertion for CaptureGlitchModal's submit should
also assert the serialized gating and expiry fields: update the
expect(mockCapture).toHaveBeenCalledWith(WORKSPACE,
expect.objectContaining({...})) payload to include gates (e.g., gates: <expected
serialized value>) and expires (e.g., expires: <expected serialized value>)
alongside title, description, severity, source, and created_by; locate the
assertion in CaptureGlitchModal.test.tsx and add the gates and expires keys
matching how the component serializes those form fields so regressions in
gates/expires mapping are caught.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 06ff95ff-771d-4aa3-8b60-dea47d722842
📒 Files selected for processing (7)
web-ui/src/__tests__/components/proof/CaptureGlitchModal.test.tsxweb-ui/src/__tests__/components/proof/ProofPage.test.tsxweb-ui/src/app/proof/page.tsxweb-ui/src/components/proof/CaptureGlitchModal.tsxweb-ui/src/components/proof/index.tsweb-ui/src/lib/api.tsweb-ui/src/types/index.ts
| async function handleSubmit() { | ||
| // Validate | ||
| if (!description.trim()) { | ||
| setError('Description is required'); | ||
| return; | ||
| } | ||
| if (selectedGates.size === 0) { | ||
| setError('Select at least one gate'); | ||
| return; | ||
| } | ||
|
|
||
| setError(null); | ||
| setSubmitting(true); | ||
|
|
||
| // Derive title from first line of description (max 80 chars) | ||
| const title = description.trim().split('\n')[0].slice(0, 80); | ||
|
|
||
| // Derive `where` from scope lines, falling back to source | ||
| const scopeLines = scopeText.split('\n').map((l) => l.trim()).filter(Boolean); | ||
| const where = scopeLines.length > 0 ? scopeLines.join(', ') : source; | ||
|
|
||
| const body: CaptureGlitchRequest = { | ||
| title, | ||
| description: description.trim(), | ||
| where, | ||
| severity, | ||
| source, | ||
| created_by: 'human', | ||
| }; |
There was a problem hiding this comment.
Required/collected form inputs are dropped before API submission.
selectedGates and expires are validated/collected but never included in CaptureGlitchRequest, so user input is discarded on submit.
Proposed fix
const body: CaptureGlitchRequest = {
title,
description: description.trim(),
where,
severity,
source,
created_by: 'human',
+ gates: Array.from(selectedGates),
+ expires: expires || null,
};// Also update web-ui/src/types/index.ts CaptureGlitchRequest
// and backend request model/handler to accept these mapped fields.Also applies to: 222-233
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web-ui/src/components/proof/CaptureGlitchModal.tsx` around lines 87 - 115,
The submit handler (handleSubmit) currently builds CaptureGlitchRequest without
including selectedGates and expires, so those validated inputs are dropped;
update handleSubmit to add selectedGates (map to an appropriate field name like
gates or affected_gates) and expires (map to expires_at or ttl) into the body
object, update the CaptureGlitchRequest type definition to include these fields,
and ensure the backend request model/handler accepts and maps those new fields
as well so user input is preserved end-to-end.
|
|
||
| return ( | ||
| <Dialog open={open} onOpenChange={(o) => { if (!o) onClose(); }}> | ||
| <DialogContent className="max-w-lg"> |
There was a problem hiding this comment.
Modal size does not meet the full-screen requirement.
DialogContent is constrained to max-w-lg; this is a centered dialog, not a full-screen modal/slide-over.
Proposed fix
- <DialogContent className="max-w-lg">
+ <DialogContent className="h-[100dvh] w-[100dvw] max-w-none rounded-none overflow-y-auto">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <DialogContent className="max-w-lg"> | |
| <DialogContent className="h-[100dvh] w-[100dvw] max-w-none rounded-none overflow-y-auto"> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web-ui/src/components/proof/CaptureGlitchModal.tsx` at line 130, The modal in
CaptureGlitchModal uses DialogContent with class "max-w-lg", producing a
centered dialog instead of a full-screen slide-over; update the DialogContent
usage in CaptureGlitchModal to remove "max-w-lg" and replace it with
full-screen/slide-over appropriate utility classes (e.g., full width/height and
no max-width) so the component renders edge-to-edge on small and large screens,
and adjust padding/margin classes as needed to preserve inner layout and
accessibility (keep the same component name CaptureGlitchModal and DialogContent
so you only change the className and related layout utilities).
Summary
Closes #568 — Phase 3.5C: Glitch Capture web UI
POST /api/v2/proof/requirements— backend was already readymutate()re-fetches the requirements table so the new REQ appears immediatelyChanges
web-ui/src/components/proof/CaptureGlitchModal.tsxweb-ui/src/components/proof/index.tsweb-ui/src/lib/api.tsproofApi.capture()method addedweb-ui/src/types/index.tsCaptureGlitchRequesttype addedweb-ui/src/app/proof/page.tsxweb-ui/src/__tests__/components/proof/CaptureGlitchModal.test.tsxweb-ui/src/__tests__/components/proof/ProofPage.test.tsxTest plan
npm test)npm run buildsucceeds — all routes cleanuv run ruff check .passesSummary by CodeRabbit
New Features
Tests