diff --git a/.github/workflows/reference-implementation.yml b/.github/workflows/reference-implementation.yml index 664b68a93..58a1e0a21 100644 --- a/.github/workflows/reference-implementation.yml +++ b/.github/workflows/reference-implementation.yml @@ -72,7 +72,6 @@ jobs: packages/polyfill-connectors/*|packages/polyfill-connectors/**|\ packages/reference-contract/*|packages/reference-contract/**|\ openspec/changes/complete-mcp-slvp-surface/*|openspec/changes/complete-mcp-slvp-surface/**|\ - docs/research/mcp-slvp-*.md|\ reference-implementation/*|reference-implementation/**|\ scripts/check-console-health-surfaces.ts|\ scripts/check-console-health-surfaces.test.ts|\ diff --git a/docs/research/ephemeral-runtime-health-separates-control-plane-capability-from-warm-instance-state-2026-07-16.md b/docs/research/ephemeral-runtime-health-separates-control-plane-capability-from-warm-instance-state-2026-07-16.md deleted file mode 100644 index 1f929866b..000000000 --- a/docs/research/ephemeral-runtime-health-separates-control-plane-capability-from-warm-instance-state-2026-07-16.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: "Scale-to-zero runtimes separate service capability from warm-instance readiness" -date: 2026-07-16 -topic: distributed-systems -tags: [health, scale-to-zero, allocator, runtime, browser-surface] -status: final -sources: [cloud-run-autoscaling, cloud-run-min-instances, aws-lambda-environments, kubernetes-probes, chatgpt-session-loss-rootcause, chatgpt-session-discriminator, retained-credential-boundary-surface-process] ---- - -## CLAIMS - -- Cloud Run services default to zero minimum instances and scale a revision to zero when it receives no traffic; a later request creates a new instance. [cloud-run-autoscaling] -- Cloud Run describes minimum instances as an optional latency optimization that keeps instances warm and incurs idle cost. A service does not need a warm instance merely to remain deployable and callable. [cloud-run-min-instances] -- AWS Lambda creates an execution environment when an on-demand function is first invoked, may reuse it after the invocation, and advises applications not to depend on an environment being long-lived. [aws-lambda-environments] -- Kubernetes readiness is an instance-level condition: a readiness probe decides whether a running Pod can receive Service traffic. It does not establish whether a separate control plane can allocate a future instance. [kubernetes-probes] -- Two isolated ChatGPT connections completed successful runs and then both reported `chatgpt_session_required` immediately after replacement Chromium containers were created. Stable profile keys and, for one connection, a reused profile mount rule out a different or empty profile as the common explanation. [chatgpt-session-loss-rootcause] -- A read-only check of the surviving replacement surface reached `/api/auth/session` with HTTP 200 but `hasUser=false`, while DOM login and app/account markers were simultaneously present. The connector's authenticated-session probe is therefore the correct discriminator; a reachable page, persisted profile, URL, title, or logged-looking DOM is not proof of authentication. [chatgpt-session-discriminator] -- The retained-surface contract deliberately preserves a credential-boundary browser process during routine idle and capacity events. After reconciliation with the replacement evidence, its current OpenSpec treats genuine process/container loss as non-green continuity uncertainty with no owner action unless a typed provider invalidation proof exists; it still does not define portable authenticated state that survives process replacement. [retained-credential-boundary-surface-process] - -## SOURCES - -**cloud-run-autoscaling** - -URL: https://docs.cloud.google.com/run/docs/about-instance-autoscaling - -Accessed: 2026-07-16 - -**cloud-run-min-instances** - -URL: https://docs.cloud.google.com/run/docs/configuring/min-instances - -Accessed: 2026-07-16 - -**aws-lambda-environments** - -URL: https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html - -Accessed: 2026-07-16 - -URL: https://docs.aws.amazon.com/lambda/latest/dg/foundation-progmodel.html - -Accessed: 2026-07-16 - -**kubernetes-probes** - -URL: https://kubernetes.io/docs/concepts/workloads/pods/probes/ - -Accessed: 2026-07-16 - -**chatgpt-session-loss-rootcause** - -Path: internal workstream report `chatgpt-session-loss-rootcause-0716.md` (not tracked in this repo) - -Accessed: 2026-07-16 - -**retained-credential-boundary-surface-process** - -Paths: `openspec/changes/retain-credential-boundary-surface-process/{proposal.md,design.md,tasks.md,specs/polyfill-runtime/spec.md}` - -Accessed: 2026-07-16 - -**chatgpt-session-discriminator** - -Path: internal workstream report `chatgpt-session-discriminator-0716/report.md` (not tracked in this repo) - -Accessed: 2026-07-16 - -## SYNTHESIS - -An allocate-on-demand runtime has at least two independent health questions: whether its control plane is currently reachable enough to accept and describe work, and whether a concrete warm instance is currently ready. Scale-to-zero systems intentionally allow the second answer to be "none" while the service remains operable. Requiring a warm instance to keep connection health green converts an optional latency/cost tradeoff into a false availability requirement. - -PDPP's dynamic browser runtime should therefore treat a successful, bounded allocator `listSurfaces()` read as current evidence that the allocator API is reachable and returned a valid inventory response. That read does not prove free capacity, successful future container creation, profile-specific startup, CDP readiness, or provider collectability; those deeper facts are established only by lease acquisition, allocator `ensureSurface`, and the run-scoped readiness probe. Active lease/surface failure remains current fail-closed evidence. A prior ready-to-released receipt remains historical proof of a particular run, never a substitute for current allocator capability and never a reason to revive a retired surface row. - -Static runtimes differ: when there is no allocation control plane that can create a replacement on demand, current instance readiness can legitimately remain load-bearing. Non-browser connectors have neither concern and must not inherit browser-runtime uncertainty. - -Credential-boundary continuity is adjacent but not reducible to health projection. A successful allocator probe, a replacement lifecycle receipt, and a prior successful run cannot prove that provider authentication crossed a browser-process boundary. The current connector/runtime interface has no generic, provider-safe export/import contract for portable authenticated state; the active retention design instead avoids ordinary replacement, while unexpected process loss remains non-green and creates no owner action without provider proof. The replacement incidents therefore require two separate outcomes: PDPP can make every replacement causally auditable without secrets, while durable session transfer remains open until the connector-runtime credential boundary owns an explicit restoration contract and proves it with forced replacement of two isolated connections. That restoration must run and pass the connector's authenticated-session probe before owner action; DOM or profile-presence heuristics cannot substitute. Health must not turn green by treating either a profile mount or an old success as that proof. diff --git a/docs/research/external-tool-connector-adapter-prior-art-2026-07-09.md b/docs/research/external-tool-connector-adapter-prior-art-2026-07-09.md deleted file mode 100644 index c50b53b54..000000000 --- a/docs/research/external-tool-connector-adapter-prior-art-2026-07-09.md +++ /dev/null @@ -1,60 +0,0 @@ -# External-tool connector adapter prior art - -Date: 2026-07-09 - -## Question - -What can a PDPP Collection Profile connector safely delegate to an existing -personal-data command-line tool, and what must remain explicit at the adapter -boundary? - -## Primary-source check - -The review pinned HPI source revision -[`13685d8`](https://github.com/karlicoss/HPI/tree/13685d8ac432cdeeb09382aa9258cb241eeffeff). -The repository was active when checked, but its modules do not share one -record identity or field vocabulary: - -- [`my.reddit.common.RedditBase`](https://github.com/karlicoss/HPI/blob/13685d8ac432cdeeb09382aa9258cb241eeffeff/src/my/reddit/common.py) - exposes `id` and `text`; it does not establish a generic `body` field. -- [`my.coding.commits.Commit`](https://github.com/karlicoss/HPI/blob/13685d8ac432cdeeb09382aa9258cb241eeffeff/src/my/coding/commits.py) - exposes `sha` and repository/message/date fields; it does not expose a - generic `id` field. - -An adapter that requires `id` for every module or copies arbitrary upstream -objects into a loose record schema therefore does not prove a conforming HPI -connector. Each mapped module needs an explicit normalization function, -manifest-conformant output, and fixtures pinned to the upstream shape it -claims to support. - -HPI remains useful prior art for delegating source parsing and local export -access to an established tool. Repository activity is not evidence that every -module is production-ready, that schemas are uniform, or that mutable source -state is reconciled. - -## Boundary conclusions - -1. The external-tool process runner may be shared. Source-specific module - selection, normalization, record identity, cursor choice, and mutable-state - reconciliation remain connector responsibilities. -2. Runtime mappings must be a closed, manifest-declared set. Arbitrary - environment-provided module/function names cannot create undeclared streams - or bypass the consent surface. -3. Output handling must be incremental and bounded. A nominal byte ceiling on - a fully buffered stdout string is not a streaming design. -4. Cancellation must terminate the complete child process tree and integrate - with the connector run's cancellation signal. Timeout-only cleanup is not - sufficient. -5. Malformed output, missing required fields, unsupported modules, and partial - stream failures need distinct Collection Profile outcomes. Invalid JSONL - cannot disappear as if it were harmless log output. -6. Mutable streams such as saved-item collections need snapshot or deletion - reconciliation. An incremental cursor alone cannot prove that removals were - observed. - -## Implication for PDPP - -An external-tool adapter is an earned reference-implementation seam, not a new -Core or Collection Profile abstraction. It is worth implementing when a real -connector proves the common process boundary end to end. The adapter should -not land first as a generic framework with no conforming consumer. diff --git a/docs/research/heb-auth-session-and-passive-collection-2026-07-14.md b/docs/research/heb-auth-session-and-passive-collection-2026-07-14.md deleted file mode 100644 index 85708fb81..000000000 --- a/docs/research/heb-auth-session-and-passive-collection-2026-07-14.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: "H-E-B account login supports passkeys and verification codes, and unauthenticated my-account traffic lands on an Incapsula-fronted OIDC login page; passive collection therefore needs a browser-session-first path plus an optional saved-sign-in-details repair path" -date: 2026-07-14 -topic: connectors -tags: [heb, login, passkey, verification-code, incapsula, oidc, browser-session, credential-capture] -status: draft -sources: [heb-help-passkey, heb-help-passkey-fallback, heb-help-phone-code, heb-help-forgot-password, heb-help-new-account, heb-my-account-redirect] ---- - -## CLAIMS - -- H-E-B's help pages confirm passkey support, including adding a passkey from account settings and using a passkey instead of a password or verification code. That means H-E-B login is not password-only, and any automation that claims to handle the whole login surface needs a browser-session handoff for passkey cases. -- H-E-B's help pages also document a 6-digit phone verification code flow for new or unrecognized devices, password problems, and phone-number updates. That is a manual challenge path, not a background-safe automatic login. -- The "forgot password" flow is email-code-driven and culminates in creating a new password. That is a distinct recovery path, not a hidden static-secret login API. -- A public unauthenticated visit to `https://www.heb.com/my-account/your-orders` redirected to an `accounts.heb.com/oidc/auth?...prompt=login...` URL and showed an Incapsula incident page. So the browser-bound entry point is real, and the login surface is protected by bot-challenge infrastructure. -- The right product model for PDPP is therefore two honest owner choices: session-only secure browser login, or saved encrypted sign-in details for automatic session repair. - -## SOURCES - -**heb-help-passkey** -URL: https://www.heb.com/help/account/how-do-i-add-a-passkey -Accessed: 2026-07-14 - -**heb-help-passkey-fallback** -URL: https://www.heb.com/help/account/if-i-add-a-passkey-can-i-still-use-a-password-or-code-to-log-in -Accessed: 2026-07-14 - -**heb-help-phone-code** -URL: https://www.heb.com/help/account/what-are-phone-verification-security-codes -Accessed: 2026-07-14 - -**heb-help-forgot-password** -URL: https://www.heb.com/help/account/i-forgot-my-password -Accessed: 2026-07-14 - -**heb-help-new-account** -URL: https://www.heb.com/help/account/how-do-i-create-a-new-account -Accessed: 2026-07-14 - -**heb-my-account-redirect** -URL: https://www.heb.com/my-account/your-orders -Observed on: 2026-07-14 -Observed landing URL: https://accounts.heb.com/oidc/auth?client_id=...&prompt=login... - -## SYNTHESIS - -H-E-B behaves like a browser-bound, challenge-prone consumer account surface with a real identity-provider login front door. Passkeys and verification codes are first-class account recovery/login mechanisms, so a connector should not pretend the whole auth problem is "fill a password once and forget it." The operationally honest PDPP shape is: - -1. probe and reuse a live browser session first, -2. if the session is dead, use encrypted sign-in details only when the owner opted into that path, -3. on passkey / code / CAPTCHA / Incapsula / unknown-UI cases, hand the browser to the owner and re-probe afterward, -4. never log or persist provider passwords in the browser/session layer. - -That fits the existing browser-session lifecycle and lets the console present a generic dual choice for any browser-bound connector that also supports static-secret capture. diff --git a/docs/research/heb-site-knowledge-2026-07-14.md b/docs/research/heb-site-knowledge-2026-07-14.md deleted file mode 100644 index 83d33a80e..000000000 --- a/docs/research/heb-site-knowledge-2026-07-14.md +++ /dev/null @@ -1,81 +0,0 @@ -# HEB (heb.com) site knowledge — mined from vana-com/data-connectors - -Date: 2026-07-14. Source: `vana-com/data-connectors/connectors/heb` (README.md, heb-playwright.json, -heb-playwright.js ~857 lines, schemas/*.json), read in full via GitHub API. That connector's -*architecture* is rejected (see design note in `design-notes/heb-connector-manifest-design-2026-07-14.md`); -this file preserves only its hard-won facts about H-E-B's website. - -## Authentication and session - -- Form-based login, no OAuth. The vana connector never fills credentials; it opens - `https://www.heb.com/my-account/your-orders` headed and has the user sign in manually, polling - every 2s until logged-in markers appear. -- Logged-OUT signals: `input[type="password"]` present, `form[action*="sign-in"|"login"]`, or URL - containing `/challenge`, `/checkpoint`, `/sign-in`, `/login`. -- Logged-IN signals: `button[aria-label*="account" i]`, `a[href*="/my-account"]`, - `button[aria-label="My account"]`. -- Session persists via the browser profile's cookies across runs; no explicit token extraction. -- No MFA/OTP flow was coded; unknown whether H-E-B accounts ever require one. - -## Bot protection: Imperva Incapsula (confirmed) - -- The `_Incapsula_Resource` marker appears in the HTML of **every** heb.com page — presence alone - is NOT a block signal (the vana author explicitly fixed this false-positive). -- A real block/challenge renders as an **empty shell**: no `h3`, no breadcrumb nav, no - `[data-testid]` elements, `document.body.children.length <= 2`, and at least one `iframe` - (the challenge document replaces all content as a single iframe). -- Secondary markers checked: `iframe[src*="captcha"]`, `[id*="captcha"]`, `[class*="captcha"]`, - URLs `geo.captcha-delivery.com` / `/challenge` / `/blocked`, titles matching - `captcha|verify|access.denied|are.you.human|security.check`. -- Recovery pattern that worked: show headed browser, let the user solve, return to headless, - sleep 5–8s jittered, retry; after 3+ consecutive blocks pause 30s first. -- Politeness that worked: 1500–2500 ms fixed waits after every navigation (pages are - client-hydrated; DOM is not ready on `load`), 400–500 ms between order pages, jittered - 1.5–3 s between product-page fetches (the surface most likely to trigger Incapsula). - -## Data surfaces (all pure DOM scraping — no usable JSON/GraphQL API was found) - -### Profile — `GET https://www.heb.com/my-account/profile` -Label→value scraping (`

` with exact text `Name`/`Email`/`Mobile number`, read -`nextElementSibling`). Delivery-address cards under `main > div > div`, detected by -`/[A-Z]{2}\s+\d{5}/`; primary flag = card text contains "Primary". - -### Order list — `GET https://www.heb.com/my-account/your-orders?page=N` -- Order links: `a[href*="/my-account/order-history/HEB"]`. **Order IDs are prefixed `HEB`** - (last path segment of the href). -- Per-card free-text regex parsing: - - date `/([A-Z][a-z]+ \d+, \d{4})/` — long-form US dates ("July 14, 2026") - - total + count `/\$(\d+\.\d+),?\s*(\d+)\s*items?/i` - - status `/Status:\s*([^\n]+)/i` - - fulfillment `/(?:Delivery to|Curbside at)\s+([^\n]+)/i` — **the curbside-vs-delivery - signal is a free-text prefix**, followed by an address/store string. -- Pagination: `nav[aria-label*="Pagination"] a[href*="page="]`, max page from `page=(\d+)`. - Global reverse-chronological pagination — NOT year-partitioned like Amazon. -- **Scope limit: only curbside/delivery orders appear in account history. In-store purchases - are structurally unreachable** (H-E-B's app has receipt-photo upload, but no web surface). - -### Order detail — `GET https://www.heb.com/my-account/order-history/{orderId}` -- Line items: `a[href*="/product-detail"]` anchors, dedup by href; - `productId = href.split('/').pop()`. -- Per-item, from the closest `

  • ` text: quantity `/Quantity:\s*([^\n.]+)/` (free text — - may be non-numeric for weighted items; unverified), price `/Price:\s*\$?([\d.]+)/` - (**unverified whether this is unit price or line total**). -- Item image URL is derived, not scraped: `https://images.heb.com/is/image/HEBGrocery/prd-small/{productId zero-padded to 9 digits}.jpg` - (full size: `https://images.heb.com/is/image/HEBGrocery/{id}-1`). Reverse-engineered CDN - convention, not documented by H-E-B. -- The detail page does NOT expose UPC, department, or nutrition — those only exist on - product-detail pages (see below). - -### Product page — `GET https://www.heb.com/product-detail/{slug}/{productId}` (NOT user data) -Kept for reference only; a per-user connector should not crawl these (catalog data, and the -highest-Incapsula-risk surface). UPC/GTIN is available via JSON-LD (`gtin12`/`gtin13`/`gtin`/ -`gtin8` in `script[type="application/ld+json"]`), meta tags, or visible-text fallback. -Nutrition Facts panel is an `

    `+`