Skip to content

Spec: Session-only scan coordination #8

Description

@devjusty

Problem Statement

Investigators and maintainers experience brittle scan coordination: the UI and React layer still treat WordPress (and homepage) as privileged, while the Scan Session model says every Capability is equal. Recent work has been a stream of sibling-state and coordination fixes. Callers and tests must learn a wide, leaky interface (scanResult, homepageResult, WordPress-only side effects inside the coordinator) instead of one Session interface. Activity-log rotation is also bolted onto the scan coordinator, further widening that surface.

Solution

Deepen scan coordination behind a Session interface only. Callers see the Scan Session plus start / run / retry, and read state via session.capabilities.<id>. Capability Outcomes (persist, log, invalidate) are declared with each Capability and invoked with injected ports when that Capability reaches a terminal status. User-facing toasts are a single Session completion notice when an execute batch finishes; status UI continues to update live from the Scan Session. Log rotation moves out of the scan coordinator. Settings vs session option dual-store stays deferred.

Domain language: see CONTEXT.md. Decision record: ADR-0001 (session-only scan coordination).

User Stories

  1. As an investigator, I want every Capability to appear as an equal peer in scan status, so that I am not taught that WordPress is “the” scan and homepage is a bolt-on.
  2. As an investigator, I want live status updates as each Capability settles, so that I can see progress without waiting for the whole batch.
  3. As an investigator, I want a single completion toast when the batch finishes, so that I am not spammed by per-Capability success toasts during concurrent runs.
  4. As an investigator, I want that completion toast to reflect the Scan Session outcome (success / partial failure), so that “complete” is not falsely tied to WordPress alone.
  5. As an investigator, I want failed Capabilities to remain visible with retry, so that I can recover without restarting the whole session.
  6. As an investigator, I want to run or retry a single Capability after the initial batch, so that Outcomes and notices still behave correctly for that narrower execute.
  7. As an investigator, I want section UIs to read Capability results from the session, so that adding a new Capability does not require new privileged context fields.
  8. As an admin investigator, I want recon and other admin Capabilities to use the same Session interface, so that admin-only probes do not invent a second coordination model.
  9. As an authenticated investigator, I want unsupported namespaces from a successful WordPress Capability to still persist when that Capability settles, so that admin follow-up is not lost because toasts were deferred.
  10. As an authenticated investigator, I want query caches for unsupported plugins / recent scans to invalidate when WordPress persistence succeeds, so that Admin views stay current without a full page reload.
  11. As an investigator, I want activity logging for Capability settlement to still happen per Capability, so that ops/debug trails are not delayed until batch end.
  12. As an investigator, I want starting a new domain scan to replace the prior Session cleanly, so that stale Outcomes and tokens from the old run do not fire.
  13. As an investigator, I want cancelling/superseding an in-flight batch (new start) to suppress the old Session completion notice, so that I do not get a toast for an abandoned run.
  14. As a maintainer, I want the public ScanResults context to omit scanResult / scanError / homepageResult / homepageIsRunning / homepageError, so that the interface cannot privilege two Capabilities.
  15. As a maintainer, I want Scan Context to remain a thin adapter over the coordinator plus settings, so that outcome logic does not live in the provider.
  16. As a maintainer, I want Capability Outcomes registered beside runners as optional onSettled(state, session, ports), so that locality stays with the Capability definition.
  17. As a maintainer, I want the coordinator to inject runtime ports (toast, log, auth, query client) when calling Outcomes, so that the registry does not own React/Auth0 at import time.
  18. As a maintainer, I want the coordinator never to hardcode wordpress (or any Capability id) for side effects, so that new Capabilities do not require coordinator edits for Outcomes.
  19. As a maintainer, I want pure wave/status logic to remain in the session engine, so that React ports do not thicken that module’s interface.
  20. As a maintainer, I want tokens and sibling merge to remain in the React coordinator, so that concurrent Capability updates stay correct under React state.
  21. As an admin, I want activity-log rotation available from Admin without going through the scan coordinator, so that scan coordination’s interface stays session-only.
  22. As a maintainer, I want existing scan settings / preferences behaviour unchanged in this change, so that the dual-store deepening can land separately later.
  23. As a maintainer, I want tests to assert Session-interface behaviour (not WordPress projections), so that regressions in privileged leakage are caught.
  24. As a maintainer, I want tests to prove Outcomes fire per terminal Capability while the completion notice fires once per execute batch, so that the toast/outcome split cannot silently regress.
  25. As a maintainer, I want Context contract tests to fail if privileged projections return, so that convenience helpers cannot creep back in.
  26. As an investigator on a multi-Capability run, I want unavailable Capabilities to still reach a terminal status and optionally run Outcomes, so that the session model stays consistent.
  27. As an investigator, I want auth-required WordPress failures to remain understandable via status UI (and the batch completion notice), so that losing the immediate WP-only error toast does not hide the failure.
  28. As a developer implementing a new Capability, I want to add runner + optional onSettled only, so that I get coordination, status, and Outcomes without touching Context projections.
  29. As a reviewer, I want ADR-0001 and CONTEXT.md vocabulary used in the PR, so that the change is judged against the recorded decision.
  30. As someone picking up deferred work, I want settings/session unification left explicitly out of this spec, so that this PR does not grow that scope.

Implementation Decisions

  • Respect ADR-0001 and CONTEXT.md terms: Scan Session, Capability, Session interface, Capability Outcome, Session completion notice.
  • Public Session interface: session, startScan, runCapability, retryCapability (plus existing settings fields on Context only — unchanged ownership).
  • Remove privileged projections from the React coordinator and Scan Context results value.
  • Call sites that still need WordPress or homepage data read session.capabilities.<id> (ScanPage already does this for WordPress in places).
  • Add optional onSettled on Capability registry entries; WordPress Outcome moves current persist/log/invalidate behaviour out of the coordinator.
  • Coordinator invokes onSettled when a Capability reaches success | failed | unavailable, with idempotency/token guards equivalent to today’s “report once” flags.
  • Session completion notice: one toast when an execute batch finishes; suppressed if the batch’s token is no longer current; copy summarizes session-level outcome, not WordPress alone.
  • Do not use per-Capability success toasts for normal completion.
  • Module shape: deepen React coordinator; keep pure session engine for waves/status; Context stays thin adapter; do not push Outcomes into the pure engine.
  • Move activity-log rotation out of the scan coordinator into an admin/activity-log module (or Admin-local mutation) and rewire App → Admin props accordingly.
  • Settings / preferences / dual-store unification: out of scope (follow-up already tracked separately).
  • No new server endpoints; no Capability placement move (WordPress stays client-side for this spec).

Testing Decisions

  • Good tests assert external behaviour through the agreed seams — Session engine transitions, coordinator Session interface, Outcome invocation via injected ports, Context contract — not private helper names or React implementation details.
  • Prefer existing seams: session engine tests, useScan hook tests, Capability registry tests, Scan Context tests; add a focused activity-log module test if rotation moves.
  • Prior art: scanSession.test.js, useScan.test.jsx, scanCapabilities.test.js, ScanContext.test.jsx.
  • Cover: privileged projections absent; Outcome called per terminal Capability with ports; completion notice once per batch; superseded token suppresses notice/Outcomes; rotateLogs no longer exported from the scan coordinator.
  • Avoid testing toast copy wording brittlely beyond “one notice at batch end” and “not WordPress-hardcoded success path.”

Out of Scope

  • Unifying scan settings and session options into one active-config module.
  • Account preference sync.
  • Moving WordPress scan execution to the server for symmetry with homepage/sitemap/recon.
  • Collapsing UI catalogs (CAPABILITY_LABELS, SCAN_SECTIONS) into the registry (separate architecture candidate).
  • Replacing mutable admin Capability context (setScanCapabilityContext) — speculative follow-up.
  • New Capabilities, credentialed scanning, batch/scheduling redesign.
  • Broad visual redesign of Scan Status UI beyond what the Session interface requires.

Further Notes

  • Architecture review top recommendation; grilled via /grill-with-docs; ADR-0001 recorded.
  • Existing design doc for configurable scan workflow remains background; this spec narrows to coordination interface deepening.
  • Triage: ready-for-agent. Next: /to-tickets to split into tracer-bullet issues with blocking edges, then /implement per ticket with fresh context.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions