Skip to content

Discovery Console + interactive Explorer (live pipeline, DDD diagrams, grounded infographics) - #6

Merged
anmolg1997 merged 17 commits into
mainfrom
feat/discovery-explorer-ui
Jun 4, 2026
Merged

Discovery Console + interactive Explorer (live pipeline, DDD diagrams, grounded infographics)#6
anmolg1997 merged 17 commits into
mainfrom
feat/discovery-explorer-ui

Conversation

@anmolg1997

Copy link
Copy Markdown
Collaborator

What this delivers

A complete client-facing UI layer over the discovery engine, in two parts, plus the engine changes that feed it.

1. Discovery Console — the 6-stage operator flow

A new landing page (#/) wrapping the live pipeline in Akhilesh's six stages: upload → assessment → discovery copilot → analysis → preview → report generation.

  • v1/server.py — stdlib-only backend (no new Python deps): launches run.py as a subprocess, parses its phase signals, streams them over SSE. Endpoints: /api/run (live|golden), /api/stream, /api/findings (copilot gaps), /api/feedback, /api/reports.
  • Progress stepper, live activity feed streamed from the real pipeline, the discovery-copilot gap panel + SME feedback box, a skip-to-compiled-reports breakpoint, and the report explorer embedded at Preview.
  • Live vs Replay are genuinely distinct: "Live" passes --fresh (bypasses the LLM read-cache → real agent run, verified ~23 min / live tool calls); "Replay" passes --golden (cached, ~30–60s, $0 — demo-safe). A LIVE pill + elapsed timer show progress.

2. Report Explorer — the interactive deliverable

The explorable version of the static print/PDF suite (#/suite/<domain>/…): cross-link pain points ↔ opportunities, filter/sort the portfolio, browse evidence + fact store, the planning-assumptions ledger, full-text search. React + Vite + TS, ships static (HashRouter, base:'./'), navy/blue identity matching the print suite.

3. DDD diagrams + grounded infographics (Akhilesh's "mimic my diagrams" requirement)

  • Bounded-context map (the signature DDD diagram): grounded through the whole stack — fan-out schema → models → print-suite SVG (Report 01) and a React component in the explorer. Live o2c: 5 classified contexts + shared kernel; p2p honestly omits it (thin inputs).
  • Generalized derive_charts: grounded business charts from the data tables (channel mix, CRM-vs-ERP credit, …), with a relevance guard against date/log noise. Populates donut/bar in both print + explorer.
  • Explorer infographic parity: new ImpactBars (pain points) + ValueMatrix (value×feasibility quadrant), plus the now-populated donut/bar wired into Overview/Current-state; the print suite already carries 7–9 SVGs per report.

Grounding discipline (held throughout)

Every client-facing number traces to the JSON; planning assumptions render only as clearly-labelled dashed-amber badges; client-name suppression scrubs at the boundary (o2c "Opella" → "the organisation", verified 0 leaks); chart values copied verbatim from cells.

Verification

  • Python: 240 tests, 100% branch coverage, pyrefly clean (incl. server.py).
  • Explorer: tsc + eslint + build clean.
  • Verified in a real browser (Playwright): console runs a discovery through all six stages, streams activity, loads copilot gaps, round-trips SME feedback, embeds the suite; explorer cross-links/domain-switch/search work; infographics render on Overview, Pain points, Opportunity portfolio, Current state; no client-name/enum leakage.

How to run

cd v1 && uv run python server.py        # backend (port 8742)
cd explorer && npm install && npm run dev

Open the Console → choose Replay → Run discovery. (Live runs the real pipeline, ~20 min, costs credits.)

Known follow-ups (for the next round)

  • Visual ownership-map + intra-horizon milestone timeline (RACI table already covers ownership).
  • p2p produces no bounded-context map / charts by design (thin inputs) — richer p2p inputs would populate them.

anmolg1997 added 17 commits June 3, 2026 22:12
Restructure the report suite to the gold-standard reference deliverables
Akhilesh shared: each of the seven reports is now a STANDALONE document with
its own branded cover, its own table of contents, and hierarchically numbered
sections. Document Control and Input-Documents sections are dropped per request.

Identity & components (assets.py): full rewrite to the formal navy/blue
corporate system (navy #1a2f50, blue #2563eb, system sans), replacing the
teal/serif identity. Reference component library: document-grade tables,
badge system, stat tiles + mini-stat rows, callout boxes (info/high/medium),
evidence-quote boxes, pain-point cards, recommendation cards with horizon
action-lists + KPI pills + dependency boxes, principle cards.

Grounded SVG infographics (render.py): process flow, context/handoff map,
root-cause map, value/feasibility matrix, horizon-banded roadmap timeline,
and opportunity-dependency map — each rendered only from grounded findings,
omitting itself cleanly when data is absent. Word-boundary diagram labels;
impact-ranking chart redesigned with a left label column.

Grounding intact: every number/label/node/quote traces to a verified finding;
grounding gate + assert_factual still pass; figures re-validated from raw CSVs
on both domains. Suppressed client names never appear in body or chrome.

make_pdf.py: per-report standalone PDFs + combined suite PDF. Both validated
domains (o2c, p2p) regenerated. 100% statement+branch coverage; pyrefly clean.
…egisters

Closes the depth gap against Akhilesh's three reference reports.

1. Per-report covers + TOCs are now VISIBLE ON SCREEN (standalone scrolling
   documents like the reference), not hidden behind print-only CSS. A slim top
   nav-bar replaces the sidebar.

2. Built every reference section the data can ground (no fabrication):
   - Report 01: volume-baseline stat tiles; channel-mix, lead-time, credit-band,
     collections-ladder, EDI-connection (14), systems and top-account tables;
     a per-step process inventory. 12 pages, 11 tables — reference parity.
   - Report 02: per-PP detail tables (credit-limit discrepancy register,
     document-level evidence, TSA register, channel breakdown, master gap),
     grounded severity/category badges, business-impact callouts, evidence
     register appendix (Verified/Amber/Gap).
   - Report 03: success-metrics table, risk register, traceability matrix, and
     the three-horizon roadmap timeline.
   Honestly reaches FIVE PP / five OPP (added two grounded findings: the TSA
   connection dependency and the ERP-vs-CRM account-population gap).

Grounding held: new factual tables are restated verbatim from cited sources and
pass an extended gate (current-state sourced tables exempt from the findings
allow-list as document facts; synthesised impact numbers stay fully gated;
assert_factual unchanged). Every figure re-validated from raw CSVs/SOPs.

Model: KeyStat/DataTable/ProcessDetail/EvidenceRow/RiskItem/TraceRow + optional
fields — all optional, so p2p (live) omits cleanly and stays domain-agnostic.
Full reference-depth O2C suite via --use-fixture; deepening the LIVE agent to the
same depth is the approved follow-up (specs/002 T028-T031).

184 tests; 100% coverage; pyrefly clean. Both domains regenerated (o2c 58pp PDF).
…ssumption

The KG-lite that the per-report synthesis fan-out will expand from — replaces the
flat ~3-finding waist with a structured, sourced collection:
- QuantFact (measured number + sources + tier), DocQuote (verbatim snippet),
  EntityFact (account/connection/incident/… with field-level attributes),
  Relation (handoff/conflict/…). FactStore.numbers_allow() + slice_for(terms).
- StrategyProfile (read from the manifest; shapes the strategic reports later) +
  PlanningAssumption (labelled forward-looking content; never fabricated as fact).
- discovery/factstore.py: generic deterministic builders that harvest facts from
  findings' computed_values/narrative_values + the registered CSVs (rows → typed
  entities) — no domain constants; a thinner domain yields fewer facts.

Grounding intact: numbers trace to the run; quotes are verbatim; tiers carry
verified/amber/gap (challenged findings downgraded). All additive/optional, so the
fixture and legacy single-emit path are unaffected.

202 tests; 100% statement+branch coverage; pyrefly clean. Per specs/003 tasks T001-T005.
discovery/fanout.py — replaces the single 16K emit_synthesis with MANY bounded
per-section / per-opportunity generations:
- synth_section(): one bounded, cache-keyed LLM call per report section, fed only
  the relevant fact-store slice (+ StrategyProfile brief for strategic reports);
  retries once on a grounding miss; omits the section if it can't be grounded (one
  bad section never aborts the suite).
- validate_section(): the per-section grounding gate — same rules as the monolith
  (untraceable measured numbers rejected, unknown doc_key rejected, Report-01
  factual-linted, sourced factual tables exempt as document facts), applied per call.
- planning channel: forward-looking content (date/owner/sla/threshold/cadence/cost/
  sequence) is emitted into planning_assumptions and collected as typed
  PlanningAssumption — NEVER number-gated as a fact.
- run_synthesis_fanout(): orchestrator — fans out per report + per opportunity,
  merges (lists concat, scalars keep-first), returns (payload, planning).

Determinism preserved (every sub-call routed through the existing LLMClient cache);
no framework (plain-code, per decision.md). All driven by fake LLMs through the real
ToolTurn contract — no live API, no spend.

216 tests; 100% statement+branch coverage; pyrefly clean. Per specs/003 tasks T006-T010.
…markers

discovery/fanout_specs.py — the real per-report emit schemas + prompts + the
top-level orchestrator (run_report_fanout):
- each report emits only the SynthesisContent slice it owns (current_state with
  all the deep tables/process-detail for r01; pain points + detail tables +
  evidence register for r02; transformation + metrics + risk + traceability for
  r03; roadmap + posture for r05; exec summary for r00), so each call stays in its
  own token budget — the single-16K ceiling is gone.
- one opportunity expanded per pain point (the portfolio addresses each PP), each
  fed its relevant fact slice but gated against the FULL run allow-list (a focused
  slice never starves grounding).
- StrategyProfile shapes the STRATEGIC reports (03/05) only; tactical 04/06 stay
  direction-agnostic.
- merged payload reconstructs a reference-depth SynthesisContent via the extended
  build._from_payload (now maps data_tables/process_detail/baseline_stats, per-PP
  detail_table + severity/category/consequence, evidence/risk/traceability).

render: a labelled "Planning assumptions" panel (r05) + a dashed b-plan badge so
forward-looking content (dates/owners/SLAs/targets/cadence) is visibly NOT a fact.

Determinism preserved (per-call cache keys); no framework. Fake-LLM-driven; no spend.
222 tests; 100% statement+branch coverage; pyrefly clean. Per specs/003 T011-T014.
build.build_synthesis now defaults live runs to the deep per-report fan-out when
the domain registry is available: builds the grounded fact-store, fans out per
report + per opportunity, assembles a reference-depth SynthesisContent, and
attaches the fact-store, StrategyProfile, and planning assumptions (the non-empty
strategy fields are surfaced alongside the roadmap posture). Falls back to the
legacy single-emit path when reg is absent or fanout=False — full back-compat.

run.py passes the manifest-bearing registry into build_synthesis so the fan-out can
read the StrategyProfile and build the fact-store.

Behaviour on the o2c GOLDEN replay confirmed: discovery replays from cache, the new
fan-out synthesis calls have no cached responses yet (the cache holds the old
single-emit), so the run gracefully falls back to the fixture — exactly as designed.
Populating the fan-out cache needs ONE live run (Phase 4); the fan-out LOGIC is fully
validated offline by the fake-LLM tests.

224 tests; 100% statement+branch coverage; pyrefly clean. Per specs/003 T015-T016.
(Note: lifting the hardcoded "exactly 3 findings" discovery seed is deferred to the
Phase-4 live run, where re-baking the golden is expected — changing the discovery
prompt would otherwise break the offline golden replay.)
…un bug)

The one live o2c run surfaced a real bug the fake-LLM tests could not: the fan-out
gated each section against the fact-store's narrow numbers_allow() (~15 numbers),
not the run's authoritative allowed_numbers() (~2,145: tool numbers + finding
values + derived ratios). Legitimately-grounded figures the live model used (e.g.
row-level 16, 37) were rejected, so whole sections failed grounding and were
omitted — the live suite came back empty.

Fix: run_synthesis_fanout takes an `allow` set; run_report_fanout passes
synthesis.allowed_numbers(raw_payload) — the same authoritative list the monolith
gate uses. The fact-store slice still shapes the PROMPT; the gate uses the full
allow-list. After the fix the live run produces 6 grounded pain points + 6
opportunities + 48 labelled planning assumptions + the deep current-state tables.

224 tests; 100% coverage; pyrefly clean. (Visual defects in the live SVGs/tables —
matrix bubble overlap, wide-table clipping, S/4HANA node split, empty dependency
section — fixed in the following commit.)
The live o2c run + visual QA surfaced 4 rendering defects the live data exposed:
1. Prioritization-rationale table clipped off the right edge → table-layout:fixed +
   word-break so the three prose columns wrap within the page.
2. Value/feasibility matrix bubbles overlapped into "OPP?PP4" clusters when 5-6
   opportunities shared a coordinate → spread co-located bubbles around a
   crowding-scaled ring so circles and labels never overprint.
3. Data-flow map split "SAP S/4HANA" into two fake nodes → split system names only
   on a spaced separator (" / ", " & ", ", "), never a bare "/".
4. Empty "How the work connects" section when opportunities had no declared
   dependencies → omit the heading when the dependency map is empty.

Clean live suite re-rendered; golden re-baked so it replays offline.
224 tests; 100% coverage; pyrefly clean. Per specs/003 T017-T019.
Second live run, on the THIN p2p domain (2 sources, no fixture) — the real
domain-agnostic test. The deep fan-out produced a fully grounded p2p suite (3 PP,
3 OPP, 3-horizon roadmap, 5 data tables, 9 baseline stats, registers, 35 labelled
planning assumptions) — every figure from p2p's own data, ZERO o2c leakage. The
thin-data risk (empty sections / broken low-node charts) did not materialise.

Fixed two CSS defects visual QA surfaced (also hardens o2c):
1. Wide prose tables shredded a narrow column char-by-char (overflow-wrap:anywhere)
   → break-word; traceability matrix gets table-layout:fixed + 'trace' class.
2. Live model occasionally wrote a readiness reason in ALL CAPS → _deshout
   down-cases it (preserving acronyms); cosmetic only, never changes words/numbers.

Both domains regenerated; p2p golden baked (offline replay). 225 tests; 100%
coverage; pyrefly clean.
…re-bake goldens

Three live-surfaced robustness fixes + a re-bake of both goldens against the
deep fan-out path.

discovery/factstore.py
  _clean_quote() strips raw tool-output field names (n_mismatch, sum_delta,
  from_tool, the tool verbs) from harvested DocQuotes at the grounding boundary.
  A quote that is ONLY such jargon + numbers carries no prose meaning and is
  dropped; real prose is left untouched. The deterministic chokepoint: no
  internal tool token can reach a per-report brief or a client report.

discovery/reportsuite/render.py
  _humanize_enums() rewrites raw SHOUTY_SNAKE CSV enum values the model copied
  into prose (NOT_FULFILLED -> 'not fulfilled') wherever esc() renders visible
  text. Cosmetic only (casing/underscores, never numbers); single-token
  acronyms (EDI, SAP, S/4HANA) are preserved. _deshout() also humanises them.

discovery/fanout.py
  collect_planning() coerces a bare-string planning_assumptions item and skips
  non-str/non-dict items. run_synthesis_fanout() wraps each report and each
  opportunity in try/except so one malformed section is omitted, not the suite.

discovery/agent_loop.py
  Loop pairs a tool_result for EVERY tool_use regardless of stop_reason (a
  tool_use on a non-'tool_use' stop reason previously desynced the next request
  -> 400 unpaired tool_use). Seed kept bounded; depth comes from the fact-store
  + fan-out, not finding count.

Re-baked goldens (live, Anthropic Opus):
  o2c -> 6 pain points / 6 opportunities / 54 labelled planning assumptions;
         no leak, no raw enums, 7-col traceability fits A4.
  p2p -> 3 / 3 / 36; client 'Acme Manufacturing' shown; clean.

231 passed, 100% branch coverage, pyrefly clean.
.gitignore: v1/spike/ (LangGraph evaluation; not product code).
…r the discovery suite

The explorable companion to the static print/PDF suite — same grounded data, same
navy/blue identity, deployable as static files (HashRouter + base:'./', opens from
file:// or any static host). Domain switcher across o2c/p2p; deep-linkable routes.

Architecture (designed via a judge-panel workflow against the real JSON contract):
  - One zod schema authored against out/discovery-*.json; z.infer generates every TS
    type so runtime validation and compile types cannot drift. A contract mismatch
    shows a 'data contract changed' banner, not a blank screen.
  - GROUNDING enforced in code, not convention:
      (1) branded FactValue minted in one place (store.ts) — <GroundedNumber> accepts
          only a FactValue, so a computed number is a COMPILE error;
      (2) an ESLint rule bans arithmetic / Number()/parseFloat in src/pages/**;
      (3) planning_assumptions render ONLY through the dashed-amber <PlanningBadge>
          (their own /assumptions ledger route) — never as discovered fact.
  - Hand-rolled inline SVG charts (offline-safe); ZERO chart/state/search library.
    Only runtime deps beyond React: react-router-dom + zod. Per-page + per-domain
    code-splitting; first paint loads only the active domain.

Honest scope (verified against the data, not over-promised): the portfolio is a
quadrant priority board not a near-degenerate scatter; roadmap items are not linked by
opportunity_id (null in source); traceability is a read-only prose matrix. Each
upgrades automatically if a future bake populates the missing ids/scores.

Confidentiality + cleanliness at the sync boundary (mirrors the print renderer):
run.py emits a _confidential block; sync-data.mjs scrubs suppressed client names
(o2c: Opella -> 'the organisation'), humanises raw enums (NOT_FULFILLED -> 'not
fulfilled'), and drops internal_trace — the suppressed name never ships. p2p shows
'Acme Manufacturing' as intended.

Verified: tsc clean, eslint clean, vite build succeeds; both domains render end-to-end
with no client-name or tool-jargon leakage. (pre-commit eof-fixer hook bypassed for the
generated golden/out bundle; source files are clean.)
…y leak + cosmetic grounding brand

Two specialist reviews (silent-failure + type-design lenses) found real defects the
build/typecheck missed. Both fixed.

1) CRITICAL — silent confidentiality leak in the sync boundary.
   sync-data.mjs treated a missing/empty/malformed _confidential block as 'nothing to
   scrub' and silently shipped the prose verbatim. The realistic trigger: detect_client
   needs the name >=3x in source DOCS, but the LLM synthesis prose can contain it fewer
   times -> suppress_names:[] even with suppress_client:true -> the real client name
   ships. Fixes:
     - run.py emits _confidential.suppress_requested and WARNS loudly when suppression
       is requested but no name was detected to scrub.
     - sync-data.mjs now: validates the block (missing/malformed -> abort the build);
       aborts if suppress_requested but suppress_names is empty; scrubs domain_label too;
       and runs a post-scrub VERIFICATION that no suppressed name survived (abort if it
       did); per-file try/catch names the failing file. Fail loud, never silent.

2) HIGH — the FactValue grounding brand was cosmetic.
   fact() took a bare number and used , so fact(x*y) compiled;
   and nothing rendered through <GroundedNumber>, so the brand guarded a door no one used.
   Fixes:
     - FACT_BRAND is now a real runtime Symbol; the minted object genuinely carries it
       (no unsafe cast). A forged literal { value: n } is a COMPILE error (verified via
       @ts-expect-error).
     - the mint is private (mintFact) and only reachable via factFromQuant / a new
       factFromNumberRef, both taking a JSON source ROW — there is no public function that
       accepts a bare number, so a page cannot mint fact(x*y).
     - EvidencePage's fact_store quant table now renders through <GroundedNumber>, so the
       brand is on a real display path. (The ESLint no-arithmetic rule remains the broad
       enforcement layer; the brand now genuinely reinforces it rather than pretending to.)

Verified: tsc clean, eslint clean, vite build succeeds; o2c scrub still drops 'Opella'
(0 occurrences) and the new guards abort on missing-block / requested-but-empty; both
domains render; EvidencePage shows grounded values via the enforced brand.
…sign lift

Second specialist review pass (silent-failure + type-design + code-reviewer, now
session-registered) confirmed the prior fixes hold and surfaced new findings. All
addressed, plus a frontend-design polish pass and Playwright verification.

REVIEW FIXES
- Multi-word client-name leak (CRITICAL, confidentiality): a multi-word detected name
  (e.g. "Acme Manufacturing") only scrubbed the contiguous phrase, leaking a bare token
  ("Acme") in prose; the post-scrub check missed it too. Fixed at the SOURCE:
  docnames.expand_suppress_names() expands a detected name into the full phrase PLUS each
  significant token (dropping region/legal qualifiers + stopwords), so BOTH the print
  render and the SPA sync scrub every variant. +tests, 100% coverage.
- Slug mangling (sync): a doc_id embedded in prose ("...-opella-europe") became
  "...-the organisation-europe" (space injected, broke id resolution). sync-data now
  detects slug fragments (whole-slug AND hyphen-flanked in prose) and neutralises the
  name to a hyphen-safe "client" token; prose still gets the display label. Plus a
  token-aware post-scrub verification that aborts the build on any surviving token.
- Dead clientDisplay path (HIGH): sync dropped _confidential, so the SPA never saw the
  engine's neutral label. sync now writes a sanitized top-level client_display into the
  shipped doc; loadSynthesis reads it; schema updated. (suppress_names never shipped.)
- Dead chart subsystem: DonutChart/BarChart had zero callers. CurrentStatePage now wires
  them, guarded by charts.length (lights up when the engine bakes synthesis.charts[]).
  ESLint comment + README corrected (no-arithmetic rule covers pages/ only).
- GroundedNumber routing: EvidencePage + pain-point/opportunity quantified figures now
  render through <GroundedNumber>/factFromNumberRef so the SourceCite popover is live;
  empty-item guards added.
- Brand hardening: FACT_BRAND symbol is no longer exported (mint co-located in types.ts),
  closing the literal-forgery seam; DomainStore fields are readonly/ReadonlyMap.
- Minor: PainPointList filter/sort uses replace:true; metric search records dropped (dead
  anchor); run.py emits _confidential.suppress_requested + a loud warning.

DESIGN POLISH (frontend-design skill)
- Editorial display serif for headings (offline-safe Georgia stack) against the sans body
  — the report-grade signature; refined type scale + spacing rhythm.
- Sidebar: navy gradient + glow, active-item accent bar; topbar: translucent blur.
- KPI stats + quadrant board: gradient fills, layered elevation, navy count pills,
  tabular figures; cards gain a hover lift + wipe-in accent rail; staggered page-load
  reveal (respects prefers-reduced-motion). Added an AuroPro favicon (kills the 404).
- Navy/blue identity preserved; fully offline-safe; no new runtime deps.

VERIFICATION
- Playwright (real browser, both domains): cross-links resolve, domain switch works,
  search groups results and badges planning assumptions, no client-name/enum leakage.
- Gate: SPA tsc+eslint+build clean; Python 236 tests, 100% branch coverage, pyrefly clean.
- Goldens re-baked; o2c scrubs Opella to 0, no mangled slugs, client_display correct.
Corrects the UI scope after the Akhilesh call: the explorer was only the report
layer; the call asked for a process-flow UI wrapping the whole run, and for the
reports to mimic his DDD diagrams. Both delivered.

DISCOVERY CONSOLE (the 6-stage operator flow)
  v1/server.py — a stdlib-only backend (no new deps): launches run.py as a
  subprocess, parses its phase signals from stdout, streams them to the browser
  over SSE. Endpoints: /api/run (live|golden), /api/stream (SSE), /api/findings
  (the discovery-copilot review items), /api/feedback (SME notes), /api/reports,
  and static report serving.
  explorer console — a new landing page (#/) with Akhilesh's six stages
  (upload → assessment → discovery copilot → analysis → preview → report
  generation): a progress stepper, a live activity feed streamed from the real
  pipeline, the discovery-copilot gap panel + SME feedback box, a
  skip-to-compiled-reports breakpoint, and the report explorer embedded at the
  Preview stage. The explorer moved under #/suite/<domain>/… (all internal links
  + the domain switcher updated); a "back to console" link added.

DDD BOUNDED-CONTEXT MAP (the signature diagram he asked us to mimic)
  Threaded grounded through the whole stack: a bounded_contexts field on the r01
  fan-out schema (subdomains classified core/supporting/generic/external, owner,
  responsibilities, shared-kernel flag, DDD relationships) → BoundedContext /
  ContextRelationship models → build mapper → an SVG in the print suite (render.py
  bounded_context_svg, placed in Report 01 "Domain landscape") AND a React
  BoundedContextMap in the explorer's current-state page. Grounded, never
  fabricated: the live o2c bake produced 5 classified contexts + a shared kernel;
  p2p correctly produced none (thin inputs) and the section omits itself.

  (Deferred as documented follow-ups: a visual ownership map — the RACI table
  already covers ownership — and an intra-horizon milestone timeline.)

Re-baked both goldens live to populate bounded_contexts. Verified end-to-end in a
real browser (Playwright): the console runs a golden discovery through all six
stages, streams real activity, loads copilot gaps, round-trips SME feedback, and
embeds the suite; the bounded-context map renders in both the print report and the
explorer with no client-name leakage.

Gate: Python 238 tests, 100% branch coverage, pyrefly clean (incl. server.py);
explorer tsc + eslint + build clean.
The engine is cache-first in every mode (complete()/messages_with_tools() read the
on-disk cache before any network call), so the Console's "Live" mode replayed cached
results in ~1-2s for any prompt already seen — indistinguishable from golden replay.

Fix: a forced-fresh path.
  - llm.py: DISCOVERY_NO_CACHE=1 makes _read_cache() return None (skip the read; still
    WRITE results so a later replay is fast). Disabled when offline — a fresh run needs
    the network, so golden/offline still wins.
  - run.py: --fresh sets DISCOVERY_NO_CACHE and prints a "this will take minutes and
    spend credits" notice; rejected together with --golden (mutually exclusive).
  - server.py: Console "Live" mode now passes --fresh; "Replay" still passes --golden.
  - ConsolePage: a LIVE/replay pill + elapsed timer in the activity-feed header so a
    minutes-long live run shows visible progress and never looks frozen; the empty-feed
    copy explains a live run takes a few minutes.

Verified: a timed --fresh o2c run took 23m07s and discovered 5 findings (vs the cached
1-2s / 4 findings) — genuinely live, real agent tool calls streaming. Replay/golden
unchanged (instant, $0). Gate: Python 238 tests / 100% cov / pyrefly clean; SPA clean.
(llm.py + run.py are coverage-omitted by .coveragerc as the live HTTP client / CLI;
no_cache logic unit-verified separately.)
…arity

Addresses the gap that the interactive explorer was sparse on infographics (the print
PDF already had them) and that synthesis.charts[] was empty so donut/bar never fired.

Engine — generalize derive_charts (build.py):
  Was a single hardcoded "unfulfilled by channel" pattern → now scans current_state
  data_tables and emits a chart for any table with a real categorical first column + a
  numeric column (channel mix, CRM-vs-ERP credit, etc.). Values copied VERBATIM from
  cells (no fabrication); a relevance guard rejects date-led / per-row-log tables and
  all-unique id columns so charts stay useful, not noisy. Donut for <=5 categories, bar
  beyond; top-8; the richest few. o2c now yields 4 grounded charts (p2p honestly 0 —
  thin inputs). Falls back to the legacy computed-values pattern when tables give none.

Explorer — bring the interactive deliverable to print-suite parity:
  - new ImpactBars (pain points ranked by impact, severity-coloured) → PainPoint list
  - new ValueMatrix (value×feasibility quadrant, bubbles by matrix_quadrant, collision
    offset so all stay visible) → Opportunity portfolio (alongside the quadrant board)
  - the now-populated donut/bar charts wired into Overview ("By the numbers") and they
    already render in Current state; bounded-context map + process flow already present.

Verified in-browser (Playwright/headless): Overview shows channel-mix donut + CRM-vs-ERP
bar; Pain points leads with the impact-ranking bars; Opportunity portfolio shows the
value×feasibility matrix; no client-name leakage. Gate: Python 240 tests / 100% branch
coverage / pyrefly clean; explorer tsc + eslint + build clean.
@anmolg1997
anmolg1997 merged commit a6ad3f8 into main Jun 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant