Commit ac39531
Phase 87: Operator UI — Stage Matrix, Failure Retry, Eligibility Trace & Priority (#233)
* docs(87): capture phase context
* docs(state): record phase 87 context session
* docs(87): UI design contract
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* docs(87): reconcile UI-SPEC typography + spacing to shipped design system
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* docs(state): record phase 87 UI-SPEC session
* docs(phase-87): add validation strategy
* docs(87): create phase plan (8 plans, 6 waves)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* docs(87): pattern map + plan-checker warning fixes + planning state
- Add 87-PATTERNS.md (24/24 analogs, 4 PR seams)
- Mark RESEARCH open questions RESOLVED (OQ-1 scope-minimal, OQ-2 recovery-parity, OQ-3 CHECK)
- Cite D-02 in 87-04/87-06 must_haves (files-table + right-pane dual home)
- Correct VALIDATION behavior #3 (enrich ELIGIBILITY_DAG is empty) + fill coverage map, approve
- Record planning complete in STATE.md; ROADMAP wave-dep annotation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* docs(phase-87): mark phase start
* feat(87-01): add StageSkip ORM sidecar model + registry
- StageSkip(TimestampMixin, Base) on stage_skip with UUID PK, FK->files.id
- UNIQUE(file_id, stage) uq_stage_skip_file_stage (<=1 skip per file/stage, D-13a)
- CHECK stage IN enrich values ck_stage_skip_enrich_only (D-10, enrich-only)
- reason NOT NULL (D-09); register StageSkip on Base.metadata + __all__
* feat(87-01): additive migration 037 create stage_skip
- revision 037, down_revision 036 (bare-number strings)
- create_table stage_skip: id PK, file_id FK->files.id, stage, reason, skipped_at
- UNIQUE(file_id, stage) + enrich-only CHECK via op.f() bare names
- mirrored downgrade drop_table; no backfill (greenfield); no SAQ jobs table
* test(87-01): migration 037 up/down + constraints + empty-diff + no-saq
- bare-number revision assert (037/036) + no-saq_jobs grep guard
- upgrade 036->037: stage_skip exists; valid enrich skip inserts
- UNIQUE(file_id,stage) rejects duplicate; enrich-only CHECK rejects 'propose'
- empty autogenerate diff for stage_skip (ORM __table_args__ mirror parity)
- mirrored downgrade drops the table
* docs(87-01): complete stage_skip marker sidecar plan
* docs(phase-87): update tracking after wave 1
* feat(87-02): Python twin — Status.SKIPPED + precedence + eligible/domain_completed threading
- Add SKIPPED member to Status (5-way: in_flight ≻ done ≻ skipped ≻ failed ≻ not_started)
- Thread skipped scalar into _analyze/_metadata/_fingerprint (after done, before failed)
- resolve_status passes skipped to the three enrich branches only; downstream ignores it
- eligible: SKIPPED enrich stage leaves the pending set; domain_completed: SKIPPED is complete
- Extend tests/shared/test_stage_resolver.py with the six D-08 precedence/eligibility behaviors
* feat(87-02): SQL twin — skipped_clause builder threaded into the three composers
- Import StageSkip; add skipped_clause(stage) correlated-exists builder (enrich-only ValueError guard)
- stage_status_case: 5-way for enrich (done ≻ skipped ≻ failed), downstream stays 4-way
- eligible_clause: append not_(skipped_clause) so a skipped stage leaves all 3 enrich pending sets
- domain_completed_clause: skipped disjunct so recovery treats a skipped stage as complete
- Add shared guard tests: skipped_clause builds for enrich, raises ValueError on downstream (D-10)
* chore(87-02): log out-of-scope drain psycopg2 fixture gap to deferred-items
* docs(87-02): complete skipped-marker derivation-threading plan
* docs(phase-87): update tracking after wave 2
* fix(test): coerce bare postgresql:// TEST_DATABASE_URL to asyncpg driver
The async_engine fixture feeds TEST_DATABASE_URL straight to
create_async_engine. A bare postgresql:// (or postgresql+psycopg2://) DSN —
the natural form operators export, matching PHAZE_QUEUE_URL — resolves
SQLAlchemy's default psycopg2 sync dialect, which the async-only stack does
not install, so every DB-fixture test dies at setup with
"No module named 'psycopg2'" (surfaced by test_drain_double_dispatch.py).
_coerce_async_dsn() normalizes sync driver prefixes to +asyncpg, rewriting
only the leading token. Adds a regression guard and resolves the Phase 87
Wave 2 deferred item (rejecting the psycopg2-binary option per the project's
async-only driver rule).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* test(87-03): extend DERIV-04 harness with skipped cells on all 3 axes
- Add seed_analysis_skipped_over_failed (skipped ≻ failed precedence cell),
seed_metadata_skipped, seed_fp_skipped mirroring the existing seed idiom.
- Add skipped cells to CASES (expect 'skipped'), ELIGIBLE_CASES (False -- leaves
the pending set), DOMAIN_COMPLETED_CASES (True -- recovery treats it complete).
- Extend load_scalars to read a skipped bool from stage_skip for each enrich
stage so the Python twin sees it; three parametrized tests cover the new cells
with zero test-body edits.
- Mutation-verified: dropping the skipped branch from stage_status_case turns the
three skipped CASES cells RED; restored to green (59 passed).
* feat(87-04): 5-bucket stage pill + 6-pill matrix with 7→6 remap
- _stage_pill.html: done/in_flight/not_started/failed/skipped tokens,
each with glyph + word + aria-label + dark: pair (WCAG 1.4.1)
- _stage_matrix.html: 6 pills Meta·FP·Analyze·Prop·Appr·Exec (Appr→review,
Exec→apply, tracklist omitted) + one-legend-per-surface
- tests/shared/test_stage_pill_render.py: buckets, order, remap landmine, legend
* test(87-03): skipped leaves all 3 enrich pending sets + recovery guards
- Add per-enrich-stage pending-set-drop tests (metadata/fingerprint/analyze):
a stage_skip marker removes a would-be-pending file from get_metadata_pending_files
/ get_fingerprint_pending_files / get_discovered_files_with_duration, proving the
~skipped conjunct in eligible_clause propagates zero-edit. Positive controls assert
the seed WOULD be pending. Mutation-verified: dropping ~skipped -> all 3 RED.
- Recovery guards (behavior 5): a force-skipped analyze/metadata file is
domain-complete and NOT re-enqueued, on both the automatic and manual (force=True)
paths. Mutation-verified: dropping the skipped_clause disjunct from
domain_completed_clause -> all 3 RED.
- Track a fingerprint recovery GAP with a strict-xfail regression guard:
_build_done_sets reads done_clause(FINGERPRINT) only (never skipped_clause), so a
force-skipped fingerprint with a surviving ledger row IS re-enqueued. Documented in
deferred-items.md (source fix is out of this tests-only parallel plan's ownership).
* test(87-03): shadow-compare stays green post-skip (additive-writer proof)
- Seed an analysis_failed file with analysis.failed_at set AND an additive
stage_skip(analyze) marker, failed_at left in place (mirrors the real write path).
- Assert run_shadow_compare reports zero hard divergences (the analysis_failed
invariant state='analysis_failed' => failed_clause(ANALYZE) still holds) and the
soft allowlist is unchanged ({fingerprinted, local_analyzing}) -- the skip needs
NO new allowlist entry (D-13d).
- Assert failed_clause(ANALYZE) is still True for the skipped-over-failed file.
- Mutation-verified teeth: simulating a non-additive writer that clears failed_at
turns all 3 tests RED -- so a later writer that tidies failed_at regresses the
Phase-79 gate loudly.
* feat(87-04): paginated, per-row-derived, degrade-safe files table
- get_files_page + _files_page_stmt: bounded (LIMIT+1 sentinel, no whole-corpus
COUNT), correlated per-page stage_status_case derivation, begin_nested SAVEPOINT
degrade to a safe empty page (T-87-11/T-87-12)
- GET /pipeline/files: stage/bucket validated against Stage/Status allowlists and
plumbed through for Plan 05 (T-87-14)
- files_table_view.html: 6-pill matrix per row, one legend per surface, record
slide-in binding, autoescaped path cell, cursor Prev/Next (no COUNT)
- test_files_page.py: bounded/no-COUNT, per-row derivation, EXPLAIN partial-index
usage, SAVEPOINT degrade
* docs(87-04): complete stage-matrix pill + paginated derived files table plan
* docs(87-03): complete skipped-marker drift-lock + behavior-guard plan
Flags the fingerprint recovery-skip gap (deferred, out of ownership) at the top of
the summary; tracked by a strict-xfail guard + deferred-items.md.
* fix(87): exclude force-skipped fingerprint from recovery re-enqueue
reenqueue._build_done_sets derived fingerprint_done from done_clause(FINGERPRINT)
only, never skipped_clause. A file the operator force-skipped for fingerprint —
domain-complete everywhere else via Plan 02's skipped_clause disjunct — was
therefore NOT in fingerprint_done, so recover_orphaned_work re-enqueued it if its
fingerprint_file ledger row survived a restart, re-driving a stage the operator
explicitly skipped (phase-87 behavior 5 gap surfaced by plan 87-03).
Derive fingerprint_done from or_(done_clause, skipped_clause): a FAILED-but-not-
skipped fingerprint still auto-retries (FAILURE_IS_TERMINAL[fingerprint] is False),
a force-SKIPPED one is excluded. Not domain_completed_clause — that couples recovery
to the terminality axis and obscures the FAIL-04 auto-retry intent. Converts the
87-03 strict-xfail tripwire to a passing guard (mutation-verified); resolves the
Wave 3 deferred item.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* docs(phase-87): update tracking after wave 3
* feat(87-05): per-stage status filter lens over the files table (UI-02/D-03)
- _status_filter_bar.html: URL-carried stage x bucket filter (hx-push-url),
failure-inclusive, one canonical surface (no separate failures page)
- files_table_view.html: include the filter bar above the table + filter-aware
empty states (unfiltered / failed-filter / other-filter copy per Copywriting Contract)
- tests/integration/test_files_filter.py: failed-filter returns only failed rows,
empty failed-filter renders the failed-filter copy, filter state is URL-carried
* feat(87-05): retire raw-enum State render + mutation-tested grep guard (UI-01)
- metadata_workspace.html: remove the {'text': f.state} raw-enum State column/cell
(the one genuine raw-internal-status render); per-stage status now lives only in the
derived _stage_matrix pill row on the files table (anti-feature table cutover)
- analyze_workspace.html: rename the derived window-progress column State -> Progress
(it never rendered a raw enum; the f.state == '...' lines are pure comparisons that
drive the load-bearing WORK-04 mid-flight signal)
- tests/shared/test_no_raw_state_render.py: grep guard scanning pipeline/partials + record
templates for {{ f.state }} / {'text': f.state} render forms; strips Jinja comments so
prose mentions don't false-positive; comparisons/sets are filtered out. Mutation-tested:
re-adding a raw f.state cell turns it RED (observation in SUMMARY)
* feat(87-06): force-skip writer endpoint (enrich-only, additive, sanitized, committed)
- POST /pipeline/files/{file_id}/skip/{stage} writes a stage_skip marker (D-08/UI-04)
- enrich-only 422 guard (D-10, T-87-18) mirrors pipeline_stages._validate_stage
- required reason (D-09, T-87-22) + sanitize_pg_text before persist (T-87-19)
- additive-only: never clears analysis.failed_at (T-87-20, shadow-compare stays green)
- commits itself (get_session never auto-commits); tests read from an INDEPENDENT session
* docs(87-05): complete failure-filter-lens + raw-state-retirement plan
* feat(87-06): single-row eligibility trace endpoint + _eligibility_trace.html
- GET /pipeline/files/{file_id}/trace/{stage} evaluates resolve_status + the REAL
eligible() for ONE file and names the single unmet blocker (UI-03, behavior 10)
- single-row file_id-scoped reads, never a corpus scan (T-87-23); degrade-safe
- verdict is authoritative eligible(); a SKIPPED upstream stays gating under the OQ-1
scope-minimal resolution (Phase 90) -- rendered honestly, not as counterfeit 'satisfied'
- _eligibility_trace.html: verdict + 4 named conjuncts, blocker highlighted, autoescaped
* feat(87-06): right-pane expanded matrix (trace triggers) + enrich-only force-skip dialog
- record_body.html: 6 keyboard-accessible stage trace triggers (hx-get the trace endpoint,
reveal _eligibility_trace.html beneath the clicked pill) — the right-pane half of D-02
- _force_skip_dialog.html: Alpine x-trap focus-trap confirm dialog with a REQUIRED reason
textarea, accent-cyan confirm, Cancel default focus; posts to the Task-1 writer
- force-skip rendered for enrich stages ONLY (metadata/fingerprint/analyze); propose/review/
apply carry NO skip affordance (approval-bypass hazard, D-10) — render-asserted with teeth
- verbatim UI-SPEC copy; 422 validation force-swapped inline; success closes + aria-live toast
* docs(87-06): complete right-pane eligibility trace + force-skip plan
* docs(phase-87): update tracking after wave 4
* feat(87-07): per-file scoped retry variants (analyze + metadata), manual-analyze preserved
- Add POST /pipeline/files/{id}/analysis-failed/retry and
/pipeline/files/{id}/metadata-failed/retry: scoped twins of the bulk endpoints,
filtered to one file through the SAME Phase-30-hardened guarded funnel
(resolve_queue_for_task -> NoActiveAgentError guard -> enqueue).
- Analyze per-file retry stays manual-only: flips ANALYSIS_FAILED -> FINGERPRINTED,
clears analysis.failed_at in one txn, commits BEFORE enqueue (Phase-81 CR-01); no
auto-loop (ELIGIBLE_AFTER_FAILURE[ANALYZE]=False, behavior 8 / T-87-24).
- Metadata per-file retry leaves the failure row (D-11); reuses response partials verbatim.
- Scoped by state/EXISTS so a non-failed/unknown id is a safe no-op (T-87-27).
- tests: per-file+bulk route through the guarded funnel; no-agent -> amber, no mutation;
behavior-8 no-auto-loop predicate; render assertions for Task 2 (-k render).
* feat(87-07): wire per-row Retry (failed cells) + bulk Retry all failed · {stage}
- files_table_view.html: per-row Retry renders ONLY on a failed metadata/analyze cell
(HTMX POST to the per-file scoped variant; @click.stop keeps it off the row record
slide-in; ack swaps into the cell result span; aria-label per the Copywriting Contract).
Bulk primary Retry all failed · {stage} on the failed-filter view of an enrich stage,
posting to the live bulk endpoint. Failed pill clears on the NEXT poll tick.
- fingerprint gets NO manual retry control (per-row or bulk): it self-retries via the
eligible_clause pending set (RESEARCH / ELIG-04).
- _stage_matrix.html: doc note pointing to the cell-level retry home (pure token, no file_id).
* docs(87-07): complete per-file + bulk retry affordances plan
* docs(phase-87): update tracking after wave 5
* feat(87-08): per-stage orphan-count helper (recovery-candidate parity, degrade-safe) + #pipeline-stats seed
- get_stage_orphan_counts: per-enrich-stage recovery-candidate count
(ledger minus live minus domain-completed minus in-flight-cloud),
reusing recover_orphaned_work's own predicate so the badge cannot drift
- SAVEPOINT degrade to all-zeros; function-local reenqueue import breaks
the cycle + preserves the control-only agent-worker boundary
- seed metadataOrphan/analyzeOrphan/fingerprintOrphan onto the dag OOB
fanout in _build_dag_context (no self-poll); base.html store defaults
- test_orphan_count.py: parity vs recovery-candidate set + exclusions
(domain-completed, force-skip, live key, in-flight cloud) + degrade
* feat(87-08): rail — amber orphan badge + priority stepper + pause/resume re-wire
- amber orphaned/stuck (recovery-candidate) badge per enrich node, bound
to $store.pipeline.{metadata,fingerprint,analyze}Orphan, role=status,
hidden at 0; rides the #pipeline-stats OOB fanout (no self-poll)
- per-enrich-stage ▲/▼ priority stepper (delta ∓10) + pause/resume toggle
re-wired to the LIVE POST /pipeline/stages/{stage}/{priority,pause,resume}
endpoints (hx-swap=none; label reads the store, refreshed each 5s poll)
- fresh markup against the live endpoints (no Phase-38 template resurrected);
interactive controls in a sub-row OUTSIDE the nav <button> (no nesting);
explicit aria-labels + the D-11 clarifying tooltip; collapsed-rail hidden
- test_rail_priority_controls.py: endpoints/deltas, aria-labels, D-11 tooltip,
orphan-badge store binding + hidden-at-0 + amber/role=status, no-nesting
* chore(87-08): log pre-existing inert x-cloak (no CSS rule) to deferred-items
* docs(87-08): complete DAG-rail orphan badge + priority/pause re-wire plan
* fix(css): define [x-cloak] display:none so cloaked elements stop flashing
Alpine v3 does not inject the [x-cloak]{display:none} rule — the app must
define it. Neither assets/src/app.css nor the compiled output defined it, so
every x-cloak in the templates (theme toggle, header, cmdk modal, record host,
agents table, and the Phase-87 rail orphan badge / Resume / Paused caption) was
inert and flashed fallback content for a few ms before x-show initialized.
Add [x-cloak] { display: none !important; } to the source CSS (compiled output
is gitignored, rebuilt via `just tailwind`). Regression guard asserts the source
rule exists. App-wide pre-existing fix surfaced by plan 87-08; resolves the Wave
6 deferred item.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* docs(phase-87): update tracking after wave 6
* fix(87): register stage_skip in table guard + de-trip dead-template guard
Two post-merge integration failures the per-plan gates could not see:
- tests/shared/models/test_core_models.py::test_all_tables_defined asserts an
exhaustive table set; migration 037's stage_skip (87-01) was never added.
- tests/shared/core/test_dead_template_guard.py::test_entry_literals_resolve_to_templates
false-tripped on a _STAGE_TRACE_LABELS comment: `_HTML_LITERAL` captures ANY
quoted *.html in router source, and `..."Exec"). Mirrors _stage_matrix.html's
pill order.` accidentally forms a quoted literal (closing " of "Exec" opens it,
the apostrophe in html's closes it). Reworded the comment to drop the quotes
and the .html token — did NOT relax the guard assertion or _HTML_LITERAL
capture, per the guard's own WR-03 contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* fix(87): force-skip writer idempotent + validate reason after sanitize
Code-review findings on the Phase-87 force_skip_stage writer:
- CR-01 (blocker): a bare session.add(StageSkip)+commit hit UNIQUE(file_id,
stage) with an unhandled IntegrityError -> HTTP 500 on any re-submit (a
normal path: _force_skip_dialog.html is not hidden after success). Switch to
pg_insert(...).on_conflict_do_nothing(index_elements=["file_id","stage"]),
mirroring insert_ledger_if_absent — the marker's existence is the desired
end state, so a duplicate is a no-op success. Mutation-verified.
- WR-01: the D-09 "reason required" blank check ran on raw input before
sanitize_pg_text, so a NUL-only reason (str.strip() does not remove NUL)
slipped through and persisted as "". Sanitize first, validate the sanitized
value.
Adds two regression guards (idempotent-duplicate, NUL-only-reason). WR-02
(hot-poll orphan-count cost) tracked as a milestone backlog item.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* docs(87): backlog WR-02 orphan-count hot-poll cost with a 2026.7.5 close gate
Phase 87 code review flagged get_stage_orphan_counts running recovery's full
ledger derivation on every 5s /pipeline/stats poll (degrade-safe but can block
on a large ledger). Genuinely a read-path perf redesign, so per the "fix inline
or backlog with a close-out gate" rule it is recorded in the ROADMAP backlog as
a MILESTONE-CLOSE GATE that must be resolved before completing 2026.7.5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* feat(87-09): surface derived files stage-matrix as a reachable rail workspace
- shell.py: add STAGE_PARTIALS['files'] static literal + a _render_stage 'files'
branch mirroring pipeline_files() context (get_files_page, page=1/size=25, no
filters); fragment/full fork inherited from _render_stage
- rail.html: add a keyboard-accessible 'Files' nav node (hx-get=/s/files) after
Summary — max-lg:sr-only label, aria-hidden glyph, title, aria-current binding
- test_shell_routes.py: add 'files' to _RAIL_STAGES (14 nodes) + fork test +
reachability/a11y test (mutation-checked)
Closes UI-01/UI-02 reachability gap: the built+tested files matrix was
unreachable (no nav entry, chrome-less on direct nav).
* docs(87): code review + verification artifacts, IN-01 doc fix, dispositions
- 87-REVIEW.md (standard-depth review) + 87-VERIFICATION.md (passed, 5/5).
- IN-01: correct the _safe_bucket_counts / get_stage_progress docstrings from
FOUR-BUCKET to FIVE-BUCKET — the code already returns the skipped bucket
(Status.SKIPPED zero-fills the count dict since 87-02); doc-only.
- deferred-items.md: record CR-01/WR-01 fixed, WR-02 backlogged (milestone gate),
IN-01 fixed, IN-02 accepted (degrades to a clear empty state).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* docs(phase-87): complete phase execution
* docs(phase-87): evolve PROJECT.md after phase completion
* docs(phase-87): add security threat verification (31/31 closed, threats_open: 0)
* docs(phase-87): update validation strategy — re-audited, 10/10 covered, wave 0 complete
* fix(87): host OOB poll-seed targets on the files workspace + add orphan seeds
Two integration defects surfaced by Phase-87 live UAT (Playwright against a seeded
local boot), both breaking the /pipeline/stats OOB seed round-trip that drives the
chrome-polled rail badges:
- The /s/files workspace (added by 87-09) reused files_table_view.html, which never
composed the shared _workspace_poll_seeds.html host that every other workspace gets
via _workspace_scaffold. So every 5s poll on /s/files logged htmx:oobErrorNoTarget
for all seeds and none of the rail counts updated. Fix: shell._render_stage("files")
sets include_poll_seeds, and files_table_view.html includes the seed host as a
SIBLING of #files-table-view (so the inner filter/pagination swaps that target
#files-table-view never duplicate the seed ids; the pipeline_files() filter fragment
omits the flag).
- _workspace_poll_seeds.html was missing the three UI-05 orphan seed targets
(dag-seed-{metadata,fingerprint,analyze}Orphan). 87-08 added them to base.html's
store + the stats-poll fanout but not to the seed host, so the amber rail orphan
badge never received the poll's OOB seed on ANY workspace (stuck at base.html 0).
Adds a durable guard (test_workspace_poll_seeds.py): every $store.pipeline key must
have a dag-seed-<key> target, and the /s/files workspace hosts the seeds while the
filter fragment does not. Mutation-verified (removing the orphan targets re-RED's it).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4M6HDCD4UpPrMuCyn1Q9H
* test(87): persist live-UAT results + 2 fixes found via Playwright
* docs(87): ship phase 87 — PR #233
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent 0717ffa commit ac39531
78 files changed
Lines changed: 10211 additions & 105 deletions
File tree
- .planning
- phases/87-operator-ui-stage-matrix-failure-retry-eligibility-trace-pri
- alembic/versions
- assets/src
- src/phaze
- enums
- models
- routers
- services
- tasks
- templates
- pipeline/partials
- record
- shell/partials
- tests
- analyze
- tasks
- fingerprint
- integration
- test_migrations
- metadata
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
112 | 112 | | |
113 | 113 | | |
114 | 114 | | |
| 115 | + | |
| 116 | + | |
115 | 117 | | |
116 | 118 | | |
117 | 119 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
83 | | - | |
| 83 | + | |
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
| |||
149 | 149 | | |
150 | 150 | | |
151 | 151 | | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
| |||
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
166 | | - | |
| 166 | + | |
167 | 167 | | |
168 | 168 | | |
169 | 169 | | |
| |||
0 commit comments