phase: 134_sync_cancel
**Phase 134 final verification pass — all green** (all 3 task files already in `complete/`; no implementation was missing) - Verified: 5-state `SyncStatus` + `cancelled` terminal (error None, final counts kept), `POST /api/sync/cancel` (202/409/403), cooperative `_check_cancelled` boundaries, shutdown `CancelledError` contract preserved, `#sync-cancel-btn` show/hide + neutral "Sync cancelled" settle, 8-branch poll tree, re-attach — code, unit, integration, and E2E layers all present - `uv run pytest --cov=app --cov-report=term-missing` → **3150 passed**, `app/` coverage **99%** (gate: >90%) - `uv run pytest tests/e2e/test_sync_cancel.py -v --no-cov` (isolated) → **3 passed** (47.6 s) - `uv run ruff check .` → clean; `uv run pyright` → 0 errors, 0 warnings **Completion criteria:** ① cancel endpoint 202/409/403 ✅ (unit + E2E) ② `cancelled` terminal reported by status, error null, counts kept ✅ (integration `test_cancel_mid_run_lands_in_cancelled_then_resyncs_clean`) ③ unrequested-cancel pin ✅ (`test_unrequested_cancel_is_never_swallowed_and_never_converts`) ④ UI lifecycle + clean re-sync (sentinel row, full 40-file import) ✅ (E2E) ⑤ suite/coverage/E2E gates ✅ ⑥ uncancelled runs byte-identical ✅ (all 3150 pre-existing tests green untouched) - Docs: README.md reviewed in full against code (pages, auth endpoints, every config default vs `app/config.py`, palette 9+8, fail-loud, cancel section) — consistent, no changes needed. AGENTS.md: fixed one pre-existing inaccuracy (rule 2 "move its file" → "move its directory" — phases are directories); everything else verified accurate - No live infrastructure touched; no deviations **Next pending phase:** `135_git_source_branch`
This commit is contained in:
@@ -2130,6 +2130,41 @@ a.answer-image:hover { text-decoration: underline; } /* flat — the caption und
|
||||
border-color: var(--err-line);
|
||||
}
|
||||
.sync-btn.is-error:hover { background: var(--err-bg); color: var(--err-ink); }
|
||||
/* Phase 134 (task 02): the Cancel control — the in-flight sync run's
|
||||
cooperative stop (visible ONLY while a SYNC run is in flight; the
|
||||
upload job is not cancellable in this phase — sources.js owns the
|
||||
show/hide, the same never-stale contract as the main button). The
|
||||
SECONDARY pill language of the house button family (the
|
||||
.kb-summary-cancel pair): transparent fill, 1px --line border,
|
||||
--ink-soft text (5.1:1 on --surface, AA), the global 3px
|
||||
:focus-visible ring, the >=44px touch floor. :disabled = the cancel
|
||||
request in flight (one request at a time — never stale); the hover
|
||||
is guarded on :not(:disabled) so a disabled button under the cursor
|
||||
never reads as actionable. The [hidden] rule keeps the attribute
|
||||
authoritative over the display above (the .kb-summary-edit pattern).
|
||||
No layout regression on the sync-control row: the row already
|
||||
flexes with a 1rem gap (desktop) and wraps (mobile, where the pill
|
||||
goes full-width like the main button — the 360px case). */
|
||||
.sync-cancel-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 44px;
|
||||
padding: 0.5rem 0.9rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--line);
|
||||
background: transparent;
|
||||
color: var(--ink-soft); /* 5.1:1 on --surface (AA) */
|
||||
font: inherit;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sync-cancel-btn:hover:not(:disabled) { background: var(--brand-soft); color: var(--brand-ink); }
|
||||
.sync-cancel-btn:disabled { opacity: 0.6; cursor: default; } /* the cancel request is in flight */
|
||||
.sync-cancel-btn[hidden] { display: none; } /* the hidden attr must beat the display above */
|
||||
.sync-icon { width: 16px; height: 16px; display: block; flex: 0 0 auto; }
|
||||
.sync-btn .sync-icon.is-spinning { animation: spin 1s linear infinite; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
@@ -5421,6 +5456,7 @@ a.answer-image:hover { text-decoration: underline; } /* flat — the caption und
|
||||
the accessible names. */
|
||||
.page-head-row { flex-wrap: wrap; }
|
||||
.sync-btn { width: 100%; }
|
||||
.sync-cancel-btn { width: 100%; } /* phase 134: the Cancel pill stacks full-width under the Sync pill (the row already wraps) */
|
||||
.sync-label { max-width: none; flex: 1 1 auto; min-width: 0; }
|
||||
.history-refresh { width: 100%; }
|
||||
.history-refresh svg { display: block; }
|
||||
|
||||
Reference in New Issue
Block a user