diff --git a/CHANGELOG.md b/CHANGELOG.md index 99d952a..cccb02b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 avoids delayed native `title=` attributes. ### Fixed +- Tessl dashboard progress during Modal scans: skip bulk `running` placeholders for + all five Tessl rows at group start; Lint and Review (Quality) now persist + `running`→terminal via `on_scanner_progress` so only the active step shows + Running. Dashboard adds status pills for `blocked`, `stale`, `interrupted`, and + `timed_out` (`tripwire-status.js`). - Live dashboard latest-state accuracy (partial slice 21): `db/schema.sql` adds `dashboard_latest_runs` view (`DISTINCT ON (item_id)`); `tripwire-live.js` queries it instead of a global `scan_runs?limit=2000` page and batches diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c0f1243..0460464 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -307,7 +307,7 @@ separate `scan_run_scanners` row (slice 46 ✅ persist scan_run `a36cad9f`): [#109](https://github.com/neomatrix369/tripwire/pull/109)) uses `tessl review run quality --json --workspace` and stamps `tessl_run_id` from `tessl review view --last --json`, then seeds in-process -`_TesslIdContext["review_quality"]` for slices 49–51 (GWT-47.5). It is orthogonal to findings and to `risk_score`. **IMPLEMENTED (UI):** slice 48 synthesises "Not Available Yet" sentinel rows for Tessl capabilities absent from the scan_run (never stored as placeholders). **IMPLEMENTED (unit, slice 49):** Scenario Generation writes a real `scan_run_scanners` row (`scenario generate` → `download` into `/evals/`, `resume_checkpoint`, mid-scan persist). **IMPLEMENTED (unit, slice 50):** Eval starts `blocked`, auto-chains after Scenario Gen when `evals/` is populated (`eval run --runs 3 -y` + `eval view`; stale on scenario re-run; project create/repair preflight). **IMPLEMENTED (unit, slice 51):** Security Review writes `"Tessl: Review (Security)"` via `_run_tessl_review("security")` after Eval; `upstream_run_ids.review_quality` is attached before invoke; expanded Security row shows linked Quality findings when that ID is populated — see +`_TesslIdContext["review_quality"]` for slices 49–51 (GWT-47.5). It is orthogonal to findings and to `risk_score`. **IMPLEMENTED (UI):** slice 48 synthesises "Not Available Yet" sentinel rows for Tessl capabilities absent from the scan_run (never stored as placeholders). **IMPLEMENTED (unit, slice 49):** Scenario Generation writes a real `scan_run_scanners` row (`scenario generate` → `download` into `/evals/`, `resume_checkpoint`, mid-scan persist). **IMPLEMENTED:** Tessl dashboard progress — `scan_app` skips bulk `running` placeholders for the Tessl group; `run_tessl` persists Lint and Review (Quality) step-by-step via `on_scanner_progress` (not only at group end). **IMPLEMENTED (unit, slice 50):** Eval starts `blocked`, auto-chains after Scenario Gen when `evals/` is populated (`eval run --runs 3 -y` + `eval view`; stale on scenario re-run; project create/repair preflight). **IMPLEMENTED (unit, slice 51):** Security Review writes `"Tessl: Review (Security)"` via `_run_tessl_review("security")` after Eval; `upstream_run_ids.review_quality` is attached before invoke; expanded Security row shows linked Quality findings when that ID is populated — see [design/tessl-5-row-expansion.md](./design/tessl-5-row-expansion.md) and slices 49–51; scenario→eval pipeline is generate → download → `eval run` on disk `evals/` (sandbox-populated; host `evals/` is not a vuln-scan input — packing diff --git a/docs/STATUS.md b/docs/STATUS.md index a6b1c18..2f130ed 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -122,6 +122,11 @@ Reachable through production entry points / config: stamp, `resume_checkpoint` + mid-scan persist via `on_scanner_progress`. Missing token → `needs_setup`; missing `.tessl-plugin/plugin.json` → `failed`. IMPLEMENTED (unit) — `sandbox/scanners.py` / `sandbox/scan_app.py` (slice 49 ✅ #112) +- Tessl dashboard mid-scan progress — `on_scanner_start` no longer bulk-inserts + all five Tessl sources as `running`; `run_tessl()` emits per-step progress for + Lint and Review (Quality) via `on_scanner_progress` (Scenario/Eval/Security + already did). Fixes frozen-all-Running appearance during Modal scans. + IMPLEMENTED — `sandbox/scanners.py` / `sandbox/scan_app.py` / `tripwire-status.js` - Tessl Eval auto-chain — `run_tessl()` emits `"Tessl: Eval"` as `blocked` before Scenario Generation, then auto-chains to `queued`→`running` when generation completes and `/evals/` has scenarios; `tessl eval run --runs 3 -y diff --git a/docs/plan/DECISIONS.md b/docs/plan/DECISIONS.md index 11a0c29..decdd8b 100644 --- a/docs/plan/DECISIONS.md +++ b/docs/plan/DECISIONS.md @@ -187,5 +187,7 @@ | 2026-08-25 | planning | Amended slices 26+28 (not new slice) | `/tw-verify` Quality column (`N/100` from `items.quality_score`) + blocked-note footer de-dupe. Quality-only metrics; amend-in-place like slice 42. Slice 30 one-line sync. Skill implementation deferred to `slice/28-*` execution. DECIDED | | 2026-08-25 | slice-28 | Waive Before-Check: 26+27 gate PASS on main | Formal Wave H gate-evidence for 26/27 remains PLANNED on `main` (Frontline PRs #78/#80 never merged to main). Substitute: Phase-1 `agent-hooks` skills + this PR publishes `docs/user-guide/frontline-output-contract.md` as output SSOT; enable/disable skills already on main. (waived: DECISIONS 2026-08-25) | | 2026-08-25 | slice-28 | Quality + footer IMPLEMENTED | Restored Frontline `guard/verify.py` onto `slice/28-tw-verify-quality`; Quality `N/100` + blocked footer; agent-hooks `tw-verify` / `tw-self-check` / `tw-disable` synced. IMPLEMENTED | +| 2026-08-25 | planning | Added slice 54 — quality score tabs | Wave J follow-on (not new wave). Embed **Quality ≥ 80** / **Rest** tabs in `Tripwire.dc.html`; skills only; threshold 80 on Tessl `items.quality_score`. Depends 42+47. Stub: `docs/plan/slices/10-J-dashboard-data-quality/slice-54-quality-score-tabs.md`. EFP continuation Path B. DECIDED | +| 2026-08-25 | planning | **SUPERSEDED** slice 54 → 42 A14–A15 | User confirmed blend into existing slice. A14 = quality tab filter (≥80 vs Rest); A15 = counts + empty-state copy. GWT-42.11–42.16; sub-task 13; slice-54 stub deleted. No new plan/wave. DECIDED | | 2026-08-25 | sync-docs | Slice 28 feature-delta discoverability | PROGRESS execution-order 🔀; ARCHITECTURE Wave H Phase 1; docs hub + reading-router-results Tessl quality ↔ `/tw-verify` Quality; STATUS PROPOSED/Future SUPERSEDED for Phase 1. APPLIED | | 2026-08-25 | sync-docs | Slice 28 Sources attribution | Discoverability: agent-hooks README, docs hub, ARCHITECTURE/STATUS Wave H, reading-router-results, slice stub, tw-verify skill intro — Quality=Tessl / Status=Cisco+Snyk. Contract + verify.py already had Sources. README/AGENTS NO_CHANGE. APPLIED | diff --git a/docs/plan/PROGRESS.md b/docs/plan/PROGRESS.md index a8701f4..94198a2 100644 --- a/docs/plan/PROGRESS.md +++ b/docs/plan/PROGRESS.md @@ -8,7 +8,7 @@ | 12 | [`12-L-…`](slices/12-L-tessl-5-row-expansion/) | **L — Tessl 5-row expansion** | 45 ✅ · 46 ✅ · 47 ✅ · 48 ✅ · 49 ✅ · 50 ✅ · **51** 🔀 · 52 📋 | 45–50 ✅ · **51** 🔀 | | 13 | [`13-M-…`](slices/13-M-llm-usage-tracking/) | **M — LLM usage / cost observability** | **53** | 📋 plan-only (ADR-0016 follow-on; not J/L/G) | | 11 | [`11-K-…`](slices/11-K-docs-ux-plain-language/) | **K — Docs UX plain language + compaction** | **44** | 🔀 | -| 10 | [`10-J-…`](slices/10-J-dashboard-data-quality/) | **J — Dashboard Data Quality Fixes** | 42 | A1–A13 ✅ | +| 10 | [`10-J-…`](slices/10-J-dashboard-data-quality/) | **J — Dashboard Data Quality Fixes** | 42 (A14–A15 📋) | A1–A13 ✅ · quality tabs delta | | 9 | [`09-I-…`](slices/09-I-landing-intro-restyle/) | **I — Landing Intro + Visual Refresh** | 41 ✅ · 43 | 41 ✅ · 43 ✅ | | 1 | [`01-A-…`](slices/01-A-live-path-gwt/) | **A — Live path + GWT** | 1 → 2 → 3 · 4 | ✅ · 4 📦 | | 2 | [`02-B-…`](slices/02-B-characterization-evidence/) | **B — Characterization + evidence sync** | 6 → 5 | ✅ | @@ -161,7 +161,7 @@ Plan", 2026-08-15). Governance blocks *merge*, not prototyping. ### J — Dashboard Data Quality Fixes | # | Slice | MoSCoW | Status | Started | Completed | Est. time | |---|-------|--------|--------|---------|-----------|-----------| -| 42 | [slice-42-dashboard-data-quality-fixes](slices/10-J-dashboard-data-quality/slice-42-dashboard-data-quality-fixes.md) | Must | ✅ PASSED (A9–A13 · [#98](https://github.com/neomatrix369/tripwire/pull/98); A1–A8 #95) | 2026-08-19 | — | ~75 min delta | +| 42 | [slice-42-dashboard-data-quality-fixes](slices/10-J-dashboard-data-quality/slice-42-dashboard-data-quality-fixes.md) | Must | 📋 PLANNED (A14–A15 delta reopen) | — | — | ~45 min delta | ### K — Docs UX plain language + compaction | # | Slice | MoSCoW | Status | Started | Completed | Est. time | @@ -198,6 +198,7 @@ Plan", 2026-08-15). Governance blocks *merge*, not prototyping. | 4 (historical) | Remotion sibling + VO assets missing | 📦 closed with demo/hackathon deferral — reinstate slice 4 if film day returns | ## Forward Roadmap +- Wave **J** (slice **42** A14–A15) dashboard quality tabs — **📋 plan-only** — embed ≥80 vs Rest triage in `prototypes/dc-dashboard/`; execute when explicitly started (`slice/42-quality-score-tabs`). - Wave **M** (slice 53) LLM usage / cost observability is **📋 plan-only** — ADR-0016 follow-on; execute when explicitly started (`slice/53-llm-usage-tracking`). - Waves **A–C**, coverage Slice 14, and Slice 17 are merged and closed. Slice 15 is retained as a deferred claim-audit artifact, not active work. - **Wave H (Frontline):** plan-only stubs 23–39 on branch `frontline-hackathon-london-2026-agent-hooks`. Execute Musts 23→32 with human tests after H1/H2 and HARD GATE at 32 before Should work. @@ -274,3 +275,5 @@ Plan", 2026-08-15). Governance blocks *merge*, not prototyping. | 2026-08-25 | slice/51-review-security | nw-review | 51 | APPROVED | software-crafter-reviewer; After-Checks ticked; still 🔨 until commit/PR | | 2026-08-25 | slice/51-review-security | clean-commit | 51 | 🔀 ON BRANCH | `6cbded7` feat + `c96c459` style; pushed; awaiting `/create-pr` | | 2026-08-25 | slice/51-review-security | sync-docs | 51 | APPLIED | Trackers 🔨→🔀; prerequisites + OPTIONAL_SCANNER_KEYS name Security; adapters heading; README/AGENTS NO_CHANGE | +| 2026-08-25 | main / plan | enhanced-flow-planner Path B | 54 | 📋 PLANNED | Dashboard quality tabs ≥80 vs Rest; Wave J; depends 42+47 | +| 2026-08-25 | planning | Blended slice 54 → 42 A14–A15 | 42 | 📋 delta reopen | Quality tabs augment slice 42 in place; slice 54 stub removed; branch `slice/42-quality-score-tabs`. DECIDED | diff --git a/docs/plan/TRAIL.md b/docs/plan/TRAIL.md index 4d7a182..b5a14d8 100644 --- a/docs/plan/TRAIL.md +++ b/docs/plan/TRAIL.md @@ -91,7 +91,7 @@ Groups are ordered by when the wave ran (or will run), not by slice number. | 8 | `slices/08-H-agent-guard-integration/` (stubs pending) | **H — Claude Code Agent Guard integration** | H0 governance → H1–H7 (hackathon plan §9 steps 0–7) | 🔨 | | 8 | [`slices/08-H-frontline-agent-hooks/`](slices/08-H-frontline-agent-hooks/) | **H — Frontline agent hooks** | 23→32 Must · 33–38 Should · 39 Could | 📋 plan-only | | 9 | [`slices/09-I-landing-intro-restyle/`](slices/09-I-landing-intro-restyle/) | **I — Landing Intro + Visual Refresh** | 41 ✅ · **43** ✅ | [#96](https://github.com/neomatrix369/tripwire/pull/96) | -| 10 | [`slices/10-J-dashboard-data-quality/`](slices/10-J-dashboard-data-quality/) | **J — Dashboard Data Quality Fixes** | 42 ✅ A1–A13 ([#98](https://github.com/neomatrix369/tripwire/pull/98)) · A1–A8 [#95](https://github.com/neomatrix369/tripwire/pull/95) | — | +| 10 | [`slices/10-J-dashboard-data-quality/`](slices/10-J-dashboard-data-quality/) | **J — Dashboard Data Quality Fixes** | 42 ✅ A1–A13 ([#98](https://github.com/neomatrix369/tripwire/pull/98)) · **A14–A15** 📋 quality tabs | — | | 11 | [`slices/11-K-docs-ux-plain-language/`](slices/11-K-docs-ux-plain-language/) | **K — Docs UX plain language + compaction** | **44** 🔀 | — | **Status legend**: `📋 PLANNED · 🔨 IN PROGRESS · ✅ PASSED · 🔀 ON BRANCH · 🔴 BLOCKED · 📦 CLOSED — DEFERRED/WON'T` @@ -246,7 +246,7 @@ Audit report: `~/.claude/plans/iterate-through-all-of-lovely-stearns.md` | # | File | Name | MoSCoW | Status | Depends on | Issue | Read time | |---|------|------|--------|--------|------------|-------|-----------| -| 42 | [slice-42-dashboard-data-quality-fixes](slices/10-J-dashboard-data-quality/slice-42-dashboard-data-quality-fixes.md) | Dashboard Data Quality Fixes (A1–A13 Tessl quality + tooltips + labels) | Must | ✅ | none | [#95](https://github.com/neomatrix369/tripwire/pull/95) · [#98](https://github.com/neomatrix369/tripwire/pull/98) | ~3 min | +| 42 | [slice-42-dashboard-data-quality-fixes](slices/10-J-dashboard-data-quality/slice-42-dashboard-data-quality-fixes.md) | Dashboard Data Quality Fixes (A1–A13 ✅ + **A14–A15** quality tabs) | Must | 📋 (A14–A15 delta) | none | [#95](https://github.com/neomatrix369/tripwire/pull/95) · [#98](https://github.com/neomatrix369/tripwire/pull/98) | ~3 min + ~45 min delta | ### K — Docs UX plain language + compaction (opened 2026-08-20) diff --git a/docs/plan/slices/10-J-dashboard-data-quality/slice-42-dashboard-data-quality-fixes.md b/docs/plan/slices/10-J-dashboard-data-quality/slice-42-dashboard-data-quality-fixes.md index fae7db5..bf672eb 100644 --- a/docs/plan/slices/10-J-dashboard-data-quality/slice-42-dashboard-data-quality-fixes.md +++ b/docs/plan/slices/10-J-dashboard-data-quality/slice-42-dashboard-data-quality-fixes.md @@ -1,9 +1,9 @@ # Slice 42 — Dashboard Data Quality Fixes -> Wave J | MoSCoW: **Must** | Status: 🔀 ON BRANCH (A9–A13 delta) | Est: ~120 min + ~60 min delta -> Depends on: none (independent; complements slices 21 and 22) -> Prior merge: [#95](https://github.com/neomatrix369/tripwire/pull/95) shipped A1–A8 ✅ — this reopen adds **A9–A13** only -> Branch: `slice/42-tessl-quality-card-surfacing` +> Wave J | MoSCoW: **Must** | Status: 📋 PLANNED (A14–A15 delta reopen) | Est: ~120 min + ~60 min (A9–A13) + ~45 min (A14–A15) +> Depends on: none (independent; complements slices 21 and 22). **A14–A15** requires A9–A13 ✅ ([#98](https://github.com/neomatrix369/tripwire/pull/98)) + slice 47 ✅ on `main`. +> Prior merge: [#95](https://github.com/neomatrix369/tripwire/pull/95) shipped A1–A8 ✅ · [#98](https://github.com/neomatrix369/tripwire/pull/98) shipped A9–A13 ✅ — this reopen adds **A14–A15** only +> Branch (when building A14–A15): `slice/42-quality-score-tabs` --- @@ -25,6 +25,11 @@ Tessl-unreachable states have no explicit indicator — operators cannot triage review” from the card face or the detail panel header. **Pile-on:** `risk 0.75` has no hover explaining that risk is weighted finding density `(3×red+1×amber)/checks`, not card colour. +**Delta (2026-08-25):** After A9–A13, operators still scroll the full skills grid to triage by Tessl +quality. Embed **Quality ≥ 80** / **Rest (below / unscored)** filter tabs in the dashboard toolbar +(skills only; threshold **80/100** on Tessl Review Quality axis). **Augment slice 42 in place** — +standalone slice 54 stub superseded (see DECISIONS 2026-08-25). + **Source**: Anomaly audit in `~/.claude/plans/iterate-through-all-of-lovely-stearns.md` and scratchpad investigation reports (2026-08-19). Delta request: enhanced-flow-planner continuation (augment existing Wave J slice — do not open a new slice). @@ -48,11 +53,13 @@ scratchpad investigation reports (2026-08-19). Delta request: enhanced-flow-plan | A11 | **FE/UX** | Medium | `risk N.NN` label has no hover explanation — operators cannot tell formula, range, or that card colour ≠ risk density | All cards showing risk | | A12 | **FE/UX** | Medium | Even after A9, quality badge has no hover explaining Tessl 0–100 meaning / provenance (parity gap vs A11 risk tooltip) | Skills showing `Q …` | | A13 | **FE/UX** | Medium | Operator-facing copy uses schema jargon (`risk_score`, ambiguous list “Score”, some locus/avail phrasing) | All dashboard surfaces | +| A14 | **FE/UX** | Medium | No quick filter to separate high Tessl quality skills (≥80) from below-threshold or unscored | Skills (Tessl-eligible) | +| A15 | **FE/UX** | Low | Quality tab counts and empty-state copy not wired when filters yield zero skills | Dashboard toolbar | > **Note**: A7 count is inflated by A1 (FE can't see the findings for out-of-window runs). > Re-audit after A1 fix to get the true DB-only count. > -> **Shipped (PR #95):** A1–A8. **Open delta:** A9–A13 (quality on top + tooltips + operator-friendly labels). +> **Shipped (PR #95):** A1–A8. **Shipped (PR #98):** A9–A13. **Open delta:** A14–A15 (quality score tabs). > > **Propagation check (2026-08-20):** Live adapter already maps `items.quality_score` → `item.quality` and into Tessl `output.quality_score` (`tripwire-live.js`). **UI top-of-card does not render it yet** — only the expanded Tessl row, and only when truthy. A9/A10/A12 close that gap. @@ -228,6 +235,48 @@ Schema / API field names may stay snake_case in code and tooltips’ technical l **Out of scope:** renaming DB columns, API fields, or developer console/`outputJson` dumps. +### GWT-42.11 — Two quality tabs render on the dashboard (A14) — **delta open** + +**Given** the operator is on the Tripwire dashboard (past intro) +**When** the dashboard toolbar renders +**Then** two quality triage tabs are visible: **Quality ≥ 80** and **Rest (below / unscored)** +**And** exactly one tab is active at a time (default: **Quality ≥ 80**) + +### GWT-42.12 — High tab lists only skills with quality ≥ 80 (A14) + +**Given** live or mock data includes skills with mixed quality scores (e.g. 92, 88, 75, 61, null) +**When** the operator selects **Quality ≥ 80** +**Then** the grid/list shows only **skill** items where `typeof quality === 'number' && quality >= 80` +**And** MCP servers are excluded regardless of other filters + +### GWT-42.13 — Rest tab lists below-threshold and unscored skills (A14) + +**Given** the same mixed dataset +**When** the operator selects **Rest (below / unscored)** +**Then** the grid/list shows only **skill** items where quality is `null`/missing **or** `< 80` +**And** items with quality exactly 79 appear in Rest, not High + +### GWT-42.14 — Quality tabs compose with search and type filters (A14) + +**Given** the operator has typed a search query and/or selected **Skills** in the existing type tabs +**When** they switch between quality tabs +**Then** results are the intersection of search + type filter + active quality tab +**And** clearing filters resets quality tab to default, search, and type filter + +### GWT-42.15 — Empty state when a quality tab has no matches (A15) + +**Given** filters yield zero skills for the active quality tab +**When** the dashboard renders +**Then** the existing filter-empty panel appears with copy naming the active quality tab +**And** **Clear filters** resets quality tab to default, search, and type filter + +### GWT-42.16 — Tab counts reflect filtered skills (A15) + +**Given** the dashboard has loaded item data +**When** quality tabs render +**Then** each tab label includes a count of matching **skills** (e.g. `≥ 80 (12)`) +**And** counts update when Live realtime refresh replaces item data + --- ## Before-Checks @@ -249,6 +298,14 @@ Schema / API field names may stay snake_case in code and tooltips’ technical l - [x] `quality-gates.sh` passes on the delta branch base before starting - [x] Coordinate with slice 43: both touch `Tripwire.dc.html` — prefer execute after #96 merges, or rebase onto 43 tip +### Delta (A14–A15) — second reopen + +- [ ] On current `main` (post-#98): confirm A9–A13 quality badges render; no quality triage tabs yet +- [ ] Confirm slice 47 ✅ on `main` (`items.quality_score` scoped to Tessl Review Quality) +- [ ] Branch `slice/42-quality-score-tabs` created from current `main` +- [ ] No concurrent edit on `Tripwire.dc.html` (check slices 48/51 if ON BRANCH — rebase if needed) +- [ ] `quality-gates.sh` passes on the delta branch base before starting + --- ## TDD Execution Order @@ -540,6 +597,28 @@ Q — = never scanned / no score yet. Q ? = scanned but Tessl did not yield a sc --- +### Sub-task 13 — Fix A14–A15: quality score triage tabs (FE/UX) — **delta** + +**Files**: +- `prototypes/dc-dashboard/tripwire-status.js` — pure helpers: `qualityMeetsThreshold(item, floor)`, `qualityTabBucket(item)`, `filterItemsByQualityTab(items, tab)` +- `prototypes/dc-dashboard/Tripwire.dc.html` — state `qualityTab` (`high` | `rest`); toolbar tab buttons; wire into `filtered` pipeline after type/search/status filters; extend `filterEmptyCopy` / `clearFilters` +- `prototypes/dc-dashboard/test/tripwire-status.test.js` — GWT-42.12–42.13 unit tests (79 vs 80 boundary, null, NaN) + +**Behaviour**: +1. Two tabs: **Quality ≥ 80** (default) and **Rest (below / unscored)** — skills-only filter (`item.type === 'skill'`). +2. Threshold **80** on `item.quality` (already mapped from `items.quality_score` in `tripwire-live.js`). +3. Tab labels include skill counts (GWT-42.16); empty state names active tab (GWT-42.15). +4. Reuse A11/A12 tooltip helpers — do not duplicate quality explanation strings. + +**Out of scope:** Supabase schema; Tessl scanner; `/tw-verify` output; canvas artifact removal. + +**Tests**: +- Unit: high bucket excludes 79 and null; rest includes 61 and null; boundary 80/79 +- Unit or manual: search + type + quality intersection (GWT-42.14) +- Manual smoke: tab chrome + counts on `http://127.0.0.1:8765/` + +--- + ## After-Checks ### Shipped (A1–A8) — historical, PR #95 @@ -570,6 +649,21 @@ Q — = never scanned / no score yet. Q ? = scanned but Tessl did not yield a sc - [x] Doc audit below complete for delta - [x] code review passed (nw-review — APPROVED 2026-08-20) +### Delta (A14–A15) — second reopen gate + +- [ ] GWT-42.11: Quality ≥ 80 / Rest tabs visible; default High +- [ ] GWT-42.12: unit — high bucket excludes 79, null +- [ ] GWT-42.13: unit — rest bucket includes null and scores < 80 +- [ ] GWT-42.14: search + type + quality intersection (unit or manual) +- [ ] GWT-42.15: empty state copy names active quality tab (manual smoke) +- [ ] GWT-42.16: tab labels show skill counts (manual smoke) +- [ ] `(cd prototypes/dc-dashboard && npm test && npm run lint)` passes +- [ ] `./scripts/quality-gates.sh` passes +- [ ] Complexity evidence: prototype dashboard **reporting** only — same policy as A9–A13 delta +- [ ] `docs/plan/gate-evidence/slice-42.json` updated for A14–A15 delta (`prior_pass` retains #98) +- [ ] Doc audit below complete for A14–A15 +- [ ] `/nw-review` APPROVED before ✅ PASSED + --- ## Code Review (nw-review, slice-42 delta) @@ -616,6 +710,12 @@ Q — = never scanned / no score yet. Q ? = scanned but Tessl did not yield a sc - [x] `CHANGELOG.md`: entry for Tessl quality card/panel surfacing + risk/quality tooltips + label polish - [x] Screenshot or smoke note: quality badge + risk hover + plain labels visible on grid + detail (optional if mock fixtures cover tests) +### Delta (A14–A15) + +- [ ] `docs/plan/DECISIONS.md`: log A14–A15 reopen; note slice 54 superseded +- [ ] `CHANGELOG.md`: entry for dashboard quality score triage tabs (≥80 vs Rest) +- [ ] `docs/STATUS.md`: IMPLEMENTED note when merged + --- ## Open Questions (for discussion before 🔨) @@ -635,20 +735,30 @@ Q — = never scanned / no score yet. Q ? = scanned but Tessl did not yield a sc 5. **Quality tooltip**: required with A9 — Tessl 0–100 skill-review explanation; parity with risk hover; not optional. 6. **Operator labels**: use GWT-42.10 glossary (`Risk density`, `Tessl quality`, plain locus/avail); no snake_case in chrome. +### Resolved (A14–A15) — USER-CONFIRMED reopen 2026-08-25 + +1. **Blend, not new slice**: quality tabs are A14–A15 delta on slice 42; slice 54 stub removed. +2. **Threshold**: 80/100 fixed (Tessl Review Quality axis); not configurable in v1. +3. **Scope**: skills only; MCP servers never appear in quality tab filters. +4. **Default tab**: Quality ≥ 80 (operators see the “good” set first). +5. **Branch**: `slice/42-quality-score-tabs` from current `main`. + --- ## Gate Status -🔀 ON BRANCH — A9–A13 implemented on `slice/42-tessl-quality-card-surfacing`. nw-review **APPROVED** 2026-08-20 — ready for ✅ PASSED + merge of PR #98. +✅ PASSED — A1–A13 merged ([#95](https://github.com/neomatrix369/tripwire/pull/95) + [#98](https://github.com/neomatrix369/tripwire/pull/98)). + +📋 PLANNED — A14–A15 quality score tabs (second delta reopen on slice 42). ```json { "slice": 42, - "gate_status": "ON_BRANCH", - "delta": "A9-A13-quality-risk-tooltips-labels", - "prior_pass": "#95", - "branch": "slice/42-tessl-quality-card-surfacing", + "gate_status": "PLANNED", + "delta": "A14-A15-quality-score-tabs", + "prior_pass": "#95 + #98", + "branch": "slice/42-quality-score-tabs", "open_questions": [], - "reopen_confirmed": "2026-08-20" + "reopen_confirmed": "2026-08-25" } ``` diff --git a/prototypes/dc-dashboard/tripwire-status.js b/prototypes/dc-dashboard/tripwire-status.js index 87c9f0d..2640e28 100644 --- a/prototypes/dc-dashboard/tripwire-status.js +++ b/prototypes/dc-dashboard/tripwire-status.js @@ -205,6 +205,10 @@ export const SCANNER_EXEC_META = { unreachable: { color: STATUS_META.error.color, label: "✗ Unreachable" }, not_applicable: { color: STATUS_META.grey.color, label: "— N/A" }, failed: { color: STATUS_META.error.color, label: "✗ Failed" }, + blocked: { color: STATUS_META.grey.color, label: "⊘ Blocked" }, + stale: { color: STATUS_META.amber.color, label: "Stale" }, + interrupted: { color: STATUS_META.amber.color, label: "Interrupted" }, + timed_out: { color: STATUS_META.amber.color, label: "Timed Out" }, not_available_yet: { color: STATUS_META.grey.color, label: "Not Available Yet", diff --git a/sandbox/scan_app.py b/sandbox/scan_app.py index c35c0b7..7843bca 100644 --- a/sandbox/scan_app.py +++ b/sandbox/scan_app.py @@ -28,7 +28,7 @@ if str(_SANDBOX_DIR) not in sys.path: sys.path.insert(0, str(_SANDBOX_DIR)) -from scanners import run_all_scanners # noqa: E402 +from scanners import TESSL_SOURCES, run_all_scanners # noqa: E402 app = modal.App("tripwire-scan") @@ -85,6 +85,10 @@ _LEGACY_SCANNER_KEYS = frozenset({"scan_run_id", "scanner_source", "status", "checks_run"}) +# Tessl rows are inserted/updated step-by-step via on_scanner_progress — not bulk +# "running" placeholders (which made all five look frozen for the whole group). +_TESSL_SOURCE_SET = frozenset(TESSL_SOURCES) + def _is_column_error(exc: Exception) -> bool: return bool(_PGRST_COLUMN_RE.search(str(exc))) @@ -209,6 +213,8 @@ def _on_scanner_start(sources): """Insert running placeholder rows so the dashboard shows progress.""" now = datetime.now(UTC).isoformat() for source in sources: + if source in _TESSL_SOURCE_SET: + continue try: supabase.table("scan_run_scanners").insert( { diff --git a/sandbox/scanners.py b/sandbox/scanners.py index a947bb1..ae51a07 100644 --- a/sandbox/scanners.py +++ b/sandbox/scanners.py @@ -1801,14 +1801,21 @@ def run_tessl( ctx = id_context if id_context is not None else _new_tessl_id_context() # --- Tessl: Lint (auth-free, synchronous) --- + lint_source = "Tessl: Lint" if not _which("npx"): - rows.append(_unreachable("Tessl: Lint", "npx not available (node/npm missing from image)")) + lint_row = _unreachable(lint_source, "npx not available (node/npm missing from image)") + rows.append(lint_row) + _emit_tessl_row_progress(on_row_progress, lint_row) else: + _emit_tessl_row_progress( + on_row_progress, + {"scanner_source": lint_source, "status": "running", "checks_run": 0}, + ) code, out, err = _run(["npx", "--yes", "tessl@latest", "skill", "lint", workdir]) console = _build_console(out, err) if code != 0: lint_row = { - "scanner_source": "Tessl: Lint", + "scanner_source": lint_source, "status": "failed", "checks_run": 0, "detail": (err or out or "lint subprocess exited non-zero").strip()[:4000], @@ -1818,7 +1825,7 @@ def run_tessl( else: checks_run, detail = _parse_tessl_lint_detail(out) lint_row = { - "scanner_source": "Tessl: Lint", + "scanner_source": lint_source, "status": "completed", "checks_run": checks_run, "detail": detail, @@ -1826,29 +1833,38 @@ def run_tessl( if console: lint_row["console_output"] = console rows.append(lint_row) + _emit_tessl_row_progress(on_row_progress, lint_row) # --- Tessl: Review (Quality) (TESSL_TOKEN + resolved workspace) --- score = None workspace: str | None = None workspace_detail = "" + review_source = "Tessl: Review (Quality)" if not os.environ.get("TESSL_TOKEN"): - rows.append(_skipped("Tessl: Review (Quality)", reason="needs_setup")) + review_row = _skipped(review_source, reason="needs_setup") + rows.append(review_row) + _emit_tessl_row_progress(on_row_progress, review_row) _update_tessl_id_context(ctx, "review_quality", None) else: workspace, workspace_detail = _resolve_tessl_workspace() if not workspace: - rows.append( - _skipped( - "Tessl: Review (Quality)", - reason="needs_setup", - detail=workspace_detail - or "Tessl workspace unresolved — set TESSL_WORKSPACE or ensure tessl login", - ) + review_row = _skipped( + review_source, + reason="needs_setup", + detail=workspace_detail + or "Tessl workspace unresolved — set TESSL_WORKSPACE or ensure tessl login", ) + rows.append(review_row) + _emit_tessl_row_progress(on_row_progress, review_row) _update_tessl_id_context(ctx, "review_quality", None) else: + _emit_tessl_row_progress( + on_row_progress, + {"scanner_source": review_source, "status": "running", "checks_run": 0}, + ) score, review_row = _run_tessl_review("quality", workdir, workspace) rows.append(review_row) + _emit_tessl_row_progress(on_row_progress, review_row) _update_tessl_id_context(ctx, "review_quality", review_row.get("tessl_run_id")) # --- Tessl: Eval (blocked until Scenario Generation completes) ---