shopflow-suite

Live Receipt Capture Runbook

Purpose

This runbook defines the repo-owned capture path for claims that still need real-world evidence before they can become public-facing support statements.

In plain language:

this is the checklist for turning a fixture-backed workflow into a reviewable live receipt bundle.

Current Scope

This runbook is currently most important for:

What Already Exists in Repo

These artifacts are already present and should be used instead of inventing a second evidence path:

What Still Requires External Evidence

The repo can now prove:

The repo cannot by itself prove:

That missing step is the external blocker.

When the repo-owned review bundle, reviewer start path, and parity checks are already clear, the submission-readiness report should expose that missing live packet under externalBlockers instead of pretending packaging is still the main blocker.

That means the reviewer/operator split should read like this:

In plain language:

once the repo-side desk work is organized, the remaining problem is no longer “which file do I open?” it becomes “someone now has to do the real live proof outside the repo.”

Wave 14 Browser Lane Contract

These commands formalize the repo-owned browser preparation path. They are useful because they turn “which browser/profile/session did I actually use?” into a reviewable JSON report instead of guesswork.

Use this env contract:

SHOPFLOW_LIVE=1
SHOPFLOW_LIVE_USER_DATA_DIR="$HOME/.cache/shopflow/browser/chrome-user-data"
SHOPFLOW_LIVE_PROFILE_DIRECTORY="Profile 1"
SHOPFLOW_LIVE_PROFILE_NAME="shopflow"
SHOPFLOW_LIVE_ATTACH_MODE="auto"
SHOPFLOW_LIVE_CDP_URL="http://127.0.0.1:9335"
SHOPFLOW_LIVE_TARGETS="safeway-home,safeway-cart,safeway-manage,fred-meyer-coupons,qfc-search,temu-search"

Command order

  1. pnpm browser:seed-profile
    • verifies that the default Chrome root is quiet before copying anything
    • now refuses to overwrite an already-existing dedicated Shopflow browser root unless you explicitly pass --replace-existing-root
    • copies only Local State plus the chosen source profile into the dedicated Shopflow root
    • rewrites the dedicated root to Profile 1 / shopflow
    • deletes copied Singleton* files before the first launch
  2. pnpm preflight:live
    • checks the requested Chrome profile, Local State, selected merchant targets, and whether the requested live lane has enough filesystem/CDP prerequisites
  3. pnpm diagnose:live
    • combines preflight plus probe state into blockers and copy-ready next steps
    • now also writes a machine-readable recommendations block with canonical profile-alignment status plus copy-ready diagnose / probe / open-browser commands
  4. pnpm probe:live
    • inspects the requested merchant tabs and writes a repo-local probe artifact
    • also writes a standardized trace bundle under .runtime-cache/live-browser/bundles/
  5. pnpm operator-capture-packet:live
    • reads the latest diagnose/probe/trace artifacts
    • writes one machine-readable operator packet under .runtime-cache/live-browser/operator-capture-packet-latest.json
    • reports which capture IDs are already capture-ready
    • reports which capture IDs are still blocked, and why
    • prefers the trace bundle’s screenshots.json manifest when mapping screenshots back to capture IDs, so page URL/title wins over fragile tab-index guesses
  6. pnpm review-candidate-records:live
    • reads the latest operator capture packet
    • writes schema-valid captured review-candidate records under .runtime-cache/live-browser/review-candidate-records-latest.json
    • keeps blocked capture IDs separate instead of promoting them prematurely
  7. pnpm review-input-template:live
    • reads the latest captured review-candidate records
    • prefers any existing reviewed-records-latest.json so the template focuses on the still-undecided captures instead of already-reviewed ones
    • writes a safe pending template under .runtime-cache/live-browser/review-input-template-latest.json
    • leaves each decision at status: "pending" so the file can exist without silently upgrading anything
  8. pnpm reviewed-records:live -- --review-input <path>
    • reads the latest captured review-candidate records plus an explicit reviewer decision JSON
    • writes schema-valid reviewed or rejected records under .runtime-cache/live-browser/reviewed-records-latest.json
    • preserves already-finalized reviewed / rejected records unless the new review input explicitly overrides that same captureId
    • still refuses to auto-review everything:
      • pending template decisions keep captured records undecided
      • action-heavy captures must include action counts in the review input before they can be upgraded to reviewed
    • run steps 5 through 8 serially when you care about the *-latest.json aliases, because each helper rewrites the latest artifact it owns
  9. pnpm open:live-browser
    • ensures the dedicated Shopflow singleton Chrome instance exists
    • if that singleton already exists, reuses it instead of second-launching the same browser root
    • only launches a remote-debuggable Chrome process when the dedicated singleton instance does not already exist
    • when SHOPFLOW_LIVE_ATTACH_MODE=browser, prefer the remote-debuggable launch path for the dedicated Shopflow browser root
    • if the machine already has more than 6 browser main processes, refuse a new debug launch and wait for other workers to release browser resources first
    • when that budget guard trips, the JSON report now also includes:
      • all detected browser main-process PIDs
      • the subset already using the requested Shopflow user-data-dir
      • the subset already advertising the requested debugging port
    • when reusing the existing Shopflow singleton instance, avoid stealing focus unless the host/browser layer offers no quieter option
    • the report now includes launchVerification.outcome, so the operator can tell whether the listener is really ready or the spawned process exited before 9335 ever became reachable
  10. pnpm close:live-browser
    • reads the current Shopflow singleton record
    • attempts a browser-owned CDP close first
    • if the browser does not exit, falls back to SIGTERM
    • only if that still fails does it use a final force kill on the recorded Shopflow singleton PID
    • removes the singleton record only when the recorded browser is truly gone

What these commands are for

What these commands are not for

Trace bundle boundary

The trace bundle is a repo-local evidence packet for debugging and operator handoff.

It is useful because it preserves:

It is still not:

Explicit review input shape

The write-reviewed-records helper expects a repo-local JSON file like:

{
  "decisions": [
    {
      "captureId": "fred-meyer-verified-scope-live-receipt",
      "status": "reviewed",
      "reviewedBy": "Shopflow QA",
      "reviewSummary": "Screenshot and verified-scope page label match the review checklist."
    },
    {
      "captureId": "safeway-cancel-live-receipt",
      "status": "reviewed",
      "reviewedBy": "Shopflow QA",
      "reviewSummary": "Manage-page screenshot and cancellation counts match the review checklist.",
      "actionSnapshot": {
        "attempted": 1,
        "succeeded": 1,
        "failed": 0,
        "skipped": 0
      }
    }
  ]
}

If you do not want to handcraft that file, first run:

pnpm review-input-template:live

That helper writes .runtime-cache/live-browser/review-input-template-latest.json with one pending decision per still-undecided captured record.

Why this matters:

Trace bundle retention and cleanup boundary

Out of scope:

In plain language:

Shopflow may leave breadcrumbs in its own notebook. It still must not pretend it owns the whole building’s storage closet.

True session lane vs clone lane

E2E temp-profile boundary

Extension smoke tests now create Chromium user-data-dir temp roots under:

This keeps interrupted test residue repo-local instead of dropping it under the system temp tree by default. Those temp profiles remain disposable and can be cleared with pnpm cleanup:runtime-cache --apply.

Important boundary:

Required Fields for a Reviewable Live Receipt

Every live receipt bundle must include:

  1. appId
  2. storeId
  3. verifiedScope
  4. pageKind
  5. actionKind or exercised capability
  6. execution date
  7. attempted / succeeded / failed counts when action-based
  8. human-readable outcome summary
  9. screenshot or equivalent visual proof

Capture Sequence

Step 1: Reconfirm Repo Baseline

Run:

pnpm lint
pnpm typecheck
pnpm test
pnpm build:wave1
pnpm build:wave2

Do not start a live capture from a dirty or unverified repo state.

Step 2: Reconfirm the Fixture-Honest Path

Run the smallest relevant checks before touching a live surface:

pnpm vitest run tests/contract/store-albertsons.contract.test.ts tests/contract/store-temu.contract.test.ts --passWithNoTests
pnpm exec playwright test tests/e2e/ext-albertsons.smoke.spec.ts tests/e2e/ext-temu.smoke.spec.ts --pass-with-no-tests

This does not create live proof.

It only confirms that the repo is still rendering the correct honesty gates.

Step 3: Perform the Live Session Outside Version Control

Use a real browser session and a real supported surface.

Examples:

Capture:

Do not store secrets, account identifiers, or sensitive raw merchant details in Git.

Step 4: Record the Capture Status in the Repo-Owned Ledger

Use the runtime evidence repository shape:

Example record shape:

{
  appId: 'ext-albertsons',
  captureId: 'safeway-subscribe-live-receipt',
  storeId: 'albertsons',
  verifiedScope: 'safeway',
  status: 'captured',
  summary: 'Safeway subscribe live receipt bundle captured and ready for review.',
  updatedAt: '2026-03-30T09:05:00.000Z',
  capturedAt: '2026-03-30T09:05:00.000Z',
  screenshotLabel: 'safeway-subscribe-success.png',
  sourcePageUrl: 'https://www.safeway.com/cart',
  sourcePageLabel: 'Live Safeway cart page',
}

If the live step has not happened yet, use status: 'missing-live-receipt' instead of pretending evidence exists.

sourcePageUrl / sourcePageLabel are optional routing metadata for operators. They help the Side Panel and Popup route back to the latest known capture surface, but they still do not turn the repo-owned ledger into the live bundle itself.

Related but separate:

Step 4.1: Review the Capture Before Calling It Release Evidence

The repo-owned ledger now uses a real workflow state instead of a single binary label:

Meaning:

Do not skip from capture-in-progress or captured straight to public wording. The repo-owned storage path now also treats this as a state-transition rule:

Ledger Integrity Rules

The repo-owned ledger is now strict about what each review state must carry.

In plain language:

the ledger is no longer allowed to say “reviewed” without also saying who reviewed it, when they reviewed it, and which proof packet they actually looked at.

Operator Packet Semantics

Treat each capture requirement like a small review packet with three separate questions:

  1. Was the packet started?
    • capture-in-progress
  2. Is there a complete packet ready for reviewer eyes?
    • captured
  3. Did a reviewer explicitly accept or reject it?
    • reviewed or rejected

This means:

Operator Surface Routing

The shared operator surface now separates the workflow into three layers:

In plain language:

the summary card is the traffic sign, the review lane is the supervisor desk, and the raw packet ledger is the filing cabinet. They serve different jobs on purpose.

The repo-owned summary layer now also prefers the next operator path in this order:

  1. finish an already started capture packet
  2. review a packet that is already captured
  3. re-capture a rejected or expired packet
  4. start a brand-new missing packet

This keeps the app-level blocker summary aligned with the raw packet ledger. In plain language:

do not abandon work that is already in flight just because another packet is still missing.

When the next actionable packet already has routing breadcrumbs recorded, the repo-owned summary may also surface:

These fields are still operator navigation aids only. They help a UI jump back to the best known packet page, but they do not upgrade the repo ledger into the reviewed live bundle.

Step 5: Only Then Consider Public Claim Review

Public wording can only move forward when all of these are true:

  1. repo verification is green
  2. the live receipt bundle exists somewhere reviewable
  3. the bundle status has been advanced to reviewed
  4. the wording stays inside verified scope
  5. the app does not overclaim beyond the captured workflow

Non-Negotiable Rules

  1. Routed fixtures are not live proof.
  2. A stored summary without screenshot evidence is not a finished live receipt.
  3. Capability-heavy Product wording stays blocked until live evidence exists and is reviewed.
  4. The repo ledger is a coordination trail, not a replacement for the real live bundle.
  5. App-level queue summaries and source-page routing are operator aids only. They do not upgrade captured into reviewed, and they do not replace the reviewed live packet outside version control.