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.
This runbook is currently most important for:
ext-albertsons
ext-temu
These artifacts are already present and should be used instead of inventing a second evidence path:
packages/runtime/src/storage/evidence-capture-repository.tstests/integration/runtime.evidence-capture-repository.test.tspackages/store-albertsons/src/adapter.tspackages/store-temu/src/temu-adapter.tstests/e2e/ext-albertsons.smoke.spec.tstests/e2e/ext-temu.smoke.spec.tspnpm preflight:livepnpm diagnose:livepnpm probe:livepnpm open:live-browser.runtime-cache/live-browser/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.”
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"
pnpm browser:seed-profile
--replace-existing-rootLocal State plus the chosen source profile into the
dedicated Shopflow rootProfile 1 / shopflowSingleton* files before the first launchpnpm preflight:live
pnpm diagnose:live
recommendations block with canonical
profile-alignment status plus copy-ready diagnose / probe /
open-browser commandspnpm probe:live
.runtime-cache/live-browser/bundles/pnpm operator-capture-packet:live
diagnose/probe/trace artifacts.runtime-cache/live-browser/operator-capture-packet-latest.jsoncapture-readyscreenshots.json manifest when mapping
screenshots back to capture IDs, so page URL/title wins over fragile
tab-index guessespnpm review-candidate-records:live
captured review-candidate records under
.runtime-cache/live-browser/review-candidate-records-latest.jsonpnpm review-input-template:live
reviewed-records-latest.json so the template focuses
on the still-undecided captures instead of already-reviewed ones.runtime-cache/live-browser/review-input-template-latest.jsonstatus: "pending" so the file can exist without
silently upgrading anythingpnpm reviewed-records:live -- --review-input <path>
reviewed or rejected records under
.runtime-cache/live-browser/reviewed-records-latest.jsonreviewed / rejected records unless the
new review input explicitly overrides that same captureIdpending template decisions keep captured records undecidedreviewed5 through 8 serially when you care about the *-latest.json
aliases, because each helper rewrites the latest artifact it ownspnpm open:live-browser
SHOPFLOW_LIVE_ATTACH_MODE=browser, prefer the remote-debuggable
launch path for the dedicated Shopflow browser root6 browser main processes, refuse a
new debug launch and wait for other workers to release browser resources
firstlaunchVerification.outcome, so the operator can
tell whether the listener is really ready or the spawned process exited
before 9335 ever became reachablepnpm close:live-browser
SIGTERMProfile 1 / shopflow live lane unless the
operator explicitly overrides it with SHOPFLOW_LIVE_*safeway-home as the stable Safeway session-health target before
judging whether cart/manage failures mean “real login lost” or “deep link drift”login_requireddeep_link_unstable
for Safeway, instead of collapsing every cart/manage failure into the same
bucket.runtime-cache/live-browser/
so the next operator does not have to reconstruct the environment from memorysummary.jsonchrome-tabs.jsonchrome-processes.jsoncdp-summary.jsonscreenshots.jsonconsole.ndjsonpageerrors.ndjsonrequestfailed.ndjsonnetwork.ndjsonscreenshots/capture-ready operator packets into reviewed
evidencepnpm exec tsx tooling/live/write-reviewed-records.ts only upgrades
captured review-candidate records when an explicit reviewer decision JSON is
supplied
status: "pending" is now the safe template state; it preserves the
captured record as undecided until the review file is actually completedpnpm browser:seed-profile should become a daily reopen
habit after the dedicated root is already healthyThe trace bundle is a repo-local evidence packet for debugging and operator handoff.
It is useful because it preserves:
It is still not:
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:
reviewed is contract-valid.runtime-cache/live-browser/*-latest.json aliases5 trace-* bundle directories7 days is disposablepnpm cleanup:runtime-cache --applyOut of scope:
/private/var/folders/**com.google.Chrome.code_sign_clone~/Library/Application Support/Google/ChromeIn plain language:
Shopflow may leave breadcrumbs in its own notebook. It still must not pretend it owns the whole building’s storage closet.
~/.cache/shopflow/browser/chrome-user-datapnpm open:live-browser, pnpm diagnose:live, and pnpm probe:live
instead of reseeding from the default Chrome root againpnpm browser:seed-profile --replace-existing-root on purposepnpm close:live-browser over a raw host-level
kill so Chrome gets a cleaner shutdown pathExtension smoke tests now create Chromium user-data-dir temp roots under:
.runtime-cache/e2e-browser/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:
Every live receipt bundle must include:
appIdstoreIdverifiedScopepageKindactionKind or exercised capabilityRun:
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.
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.
Use a real browser session and a real supported surface.
Examples:
schedule_save_subscribeschedule_save_cancelCapture:
Do not store secrets, account identifiers, or sensitive raw merchant details in Git.
Use the runtime evidence repository shape:
shopflow.liveEvidenceshopflow.liveEvidence.<appId>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:
The repo-owned ledger now uses a real workflow state instead of a single binary label:
missing-live-receiptcapture-in-progresscapturedreviewedrejectedexpiredMeaning:
capture-in-progress means the operator packet is being assembled but is not reviewable yetcaptured means the operator packet exists and is waiting for reviewreviewed means the packet passed review and can support release decisioningrejected means the packet was recorded but failed reviewexpired means the packet once existed, but is no longer trustworthy enough
for release decisioningDo 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:
capture-in-progress must advance to captured before it can ever become reviewedcaptured is explicitly review-pending, not release evidencereviewed is the first state that can support release decisioningrejected and expired both mean recapture is required before another review passThe repo-owned ledger is now strict about what each review state must carry.
captured
capturedAtscreenshotLabelreviewed
capturedAtreviewedAtreviewedByreviewSummaryscreenshotLabelrejected
reviewedAtreviewNotesexpired
expiresAtIn 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.
Treat each capture requirement like a small review packet with three separate questions:
capture-in-progresscapturedreviewed or rejectedThis means:
The shared operator surface now separates the workflow into three layers:
Evidence overview
capture-in-progress packets land here firstReview lane
captured, reviewed, rejected, and expired packets land here so operators can see review outcomes, not just generic blockersRaw packet ledger
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:
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:
nextSourcePageUrlnextSourcePageLabelnextSourceRouteLabelThese 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.
Public wording can only move forward when all of these are true:
reviewedCapability-heavy Product wording stays blocked until live evidence exists
and is reviewed.captured into reviewed, and they do not replace the
reviewed live packet outside version control.