[
  {
    "id": "fig-context",
    "question": "Who is outside Product Resolver, and which part of the system serves them?",
    "takeaway": "Collectors only acquire evidence. The resolution service is the only place that accepts facts. The product service only presents an authorized, applied release.",
    "status": "Proposed service boundaries (ADR-001). Not implemented. Workbench pages demonstrate the split; they are not a production deployment.",
    "text": [
      "Source sites and feeds are external. Collectors fetch them with their own egress.",
      "Collectors submit captures to the resolution service and stop there.",
      "Operators send human decisions to the resolution service.",
      "The resolution service emits accepted-change events to the product service.",
      "Public readers query the product service, never the resolver database."
    ],
    "nodes": [
      {
        "id": "src",
        "label": "Source sites",
        "sub": "Stores, APIs and feeds",
        "kind": "external",
        "col": 0,
        "row": 0
      },
      {
        "id": "col",
        "label": "Collectors",
        "sub": "Acquire evidence only",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "res",
        "label": "Resolution service",
        "sub": "Interpret and accept",
        "kind": "service",
        "col": 2,
        "row": 0
      },
      {
        "id": "prod",
        "label": "Product service",
        "sub": "Apply and present",
        "kind": "service",
        "col": 3,
        "row": 0
      },
      {
        "id": "read",
        "label": "Public readers",
        "sub": "Released answers only",
        "kind": "human",
        "col": 4,
        "row": 0
      },
      {
        "id": "ops",
        "label": "Operators",
        "sub": "Review and release",
        "kind": "human",
        "col": 2,
        "row": 1
      }
    ],
    "edges": [
      {
        "from": "src",
        "to": "col",
        "kind": "sync",
        "label": "fetch"
      },
      {
        "from": "col",
        "to": "res",
        "kind": "sync",
        "label": "capture"
      },
      {
        "from": "res",
        "to": "prod",
        "kind": "async",
        "label": "accepted events"
      },
      {
        "from": "prod",
        "to": "read",
        "kind": "sync",
        "label": "released read"
      },
      {
        "from": "ops",
        "to": "res",
        "kind": "sync",
        "label": "decisions"
      }
    ]
  },
  {
    "id": "fig-ownership",
    "question": "What does each part own, and where does its job stop?",
    "takeaway": "The resolver writes object storage and its database separately. The object put stays outside the SQL transaction. A neighbor may read an event. It may not write the owner’s tables.",
    "status": "Established split of responsibilities. Schema placement and roles are proposed (ADR-002, ADR-003). cat_primary remains a proposal.",
    "text": [
      "Collectors own local cursors only and stop when a receipt is durable.",
      "The resolver puts bytes in the evidence store and, separately, commits its own schema.",
      "Object storage is not inside the SQL transaction, and it does not write the database.",
      "The product service owns inbox rows and projections and stops at an applied release.",
      "Operator and consumer interfaces own no catalog records."
    ],
    "nodes": [
      {
        "id": "col",
        "label": "Collectors",
        "sub": "Own local cursors. Stop at the receipt.",
        "kind": "service",
        "col": 0,
        "row": 0
      },
      {
        "id": "res",
        "label": "Resolution service",
        "sub": "Owns evidence, facts, decisions, outbox.",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "prod",
        "label": "Product service",
        "sub": "Owns projections. Never mints identity.",
        "kind": "service",
        "col": 3,
        "row": 0
      },
      {
        "id": "ui",
        "label": "Operator and reader UIs",
        "sub": "Own no records. Call one API each.",
        "kind": "human",
        "col": 4,
        "row": 0
      },
      {
        "id": "ev",
        "label": "Evidence store",
        "sub": "Outside the SQL transaction",
        "kind": "storage",
        "col": 1,
        "row": 1
      },
      {
        "id": "db",
        "label": "Resolver schema",
        "sub": "resolver_app may write",
        "kind": "db",
        "col": 2,
        "row": 1
      },
      {
        "id": "pdb",
        "label": "Product schema",
        "sub": "product_app may write",
        "kind": "db",
        "col": 3,
        "row": 1
      }
    ],
    "edges": [
      {
        "from": "col",
        "to": "res",
        "kind": "sync",
        "label": "receipt ends it"
      },
      {
        "from": "res",
        "to": "prod",
        "kind": "async",
        "label": "events only"
      },
      {
        "from": "prod",
        "to": "ui",
        "kind": "sync",
        "label": "reads"
      },
      {
        "from": "res",
        "to": "ev",
        "kind": "storage",
        "label": "put bytes"
      },
      {
        "from": "res",
        "to": "db",
        "kind": "tx",
        "label": "SQL commit"
      },
      {
        "from": "prod",
        "to": "pdb",
        "kind": "tx",
        "label": "apply"
      }
    ]
  },
  {
    "id": "fig-journey",
    "question": "What happens to one fictional Lumen capture before a reader can see it?",
    "takeaway": "cap_lum_0310 stays evidence until acceptance. evt_0142 is the private listing-mapping correction. Readers see the listing only after a separate release is applied.",
    "status": "Prototype walkthrough of a fictional fixture. The path is the proposed design, not a production trace.",
    "text": [
      "A collector records the Lumen listing and the time the page was observed.",
      "The deterministic parser misses the heatsink field. The HTML artifact is kept.",
      "An optional model proposes a claim. A second model may advise. Neither accepts.",
      "A reviewer creates itm_ssd_p5_1t_hs. evt_0142 is listing_mapping.corrected, not a release.",
      "The product service may apply that private mapping. Readers see it only after a separately authorized release is applied."
    ],
    "nodes": [
      {
        "id": "cap",
        "label": "Capture cap_lum_0310",
        "sub": "Fictional Lumen listing page",
        "kind": "state",
        "col": 0,
        "row": 0
      },
      {
        "id": "parse",
        "label": "Deterministic parse",
        "sub": "pa_lumen_product@0.9.1 misses heatsink",
        "kind": "service",
        "col": 0,
        "row": 1
      },
      {
        "id": "llm",
        "label": "LLM extraction",
        "sub": "Proposes a claim. Optional.",
        "kind": "model",
        "col": 0,
        "row": 2
      },
      {
        "id": "adv",
        "label": "Advisory review",
        "sub": "Suggests. Cannot decide.",
        "kind": "model",
        "col": 0,
        "row": 3
      },
      {
        "id": "hum",
        "label": "Human decision dec_0141",
        "sub": "Creates the heatsink variant",
        "kind": "human",
        "col": 0,
        "row": 4
      },
      {
        "id": "acc",
        "label": "Accepted privately",
        "sub": "Facts and outbox commit together",
        "kind": "db",
        "col": 0,
        "row": 5
      },
      {
        "id": "map",
        "label": "Apply mapping privately",
        "sub": "evt_0142 is listing_mapping.corrected",
        "kind": "service",
        "col": 0,
        "row": 6
      },
      {
        "id": "priv",
        "label": "Readers still see the old release",
        "sub": "A correction is not public",
        "kind": "state",
        "col": 0,
        "row": 7
      },
      {
        "id": "pub",
        "label": "Authorized release",
        "sub": "Separate command and event",
        "kind": "gate",
        "col": 0,
        "row": 8
      },
      {
        "id": "rel",
        "label": "Apply the release",
        "sub": "Only this makes the change readable",
        "kind": "service",
        "col": 0,
        "row": 9
      },
      {
        "id": "show",
        "label": "Reader sees the listing",
        "sub": "Only the released URL and fields",
        "kind": "human",
        "col": 0,
        "row": 10
      }
    ],
    "edges": [
      {
        "from": "cap",
        "to": "parse",
        "kind": "async",
        "label": "job"
      },
      {
        "from": "parse",
        "to": "llm",
        "kind": "async",
        "label": "fallback"
      },
      {
        "from": "llm",
        "to": "adv",
        "kind": "async",
        "label": "case"
      },
      {
        "from": "adv",
        "to": "hum",
        "kind": "sync",
        "label": "advice"
      },
      {
        "from": "hum",
        "to": "acc",
        "kind": "tx",
        "label": "accept"
      },
      {
        "from": "acc",
        "to": "map",
        "kind": "async",
        "label": "private events"
      },
      {
        "from": "map",
        "to": "priv",
        "kind": "sync",
        "label": "still private"
      },
      {
        "from": "priv",
        "to": "pub",
        "kind": "sync",
        "label": "later, separate"
      },
      {
        "from": "pub",
        "to": "rel",
        "kind": "async",
        "label": "release event"
      },
      {
        "from": "rel",
        "to": "show",
        "kind": "sync",
        "label": "allowlist"
      }
    ]
  },
  {
    "id": "fig-intake",
    "question": "What must be durable before the collector receives a receipt?",
    "takeaway": "Store the artifact before the database commit. Enqueue only after that commit. A repeated submission returns through intake to the existing receipt and does not go straight to the queue.",
    "status": "Proposed intake flow (AW-02). The Pantry & Pour fixture cap_pp_feed_1011 is the deterministic stand-in. Not implemented.",
    "text": [
      "The collector declares the artifact checksum and receives an upload target.",
      "Object storage stores the bytes if that checksum is absent. That put is before the SQL commit.",
      "PostgreSQL then commits the capture, receipt and job row. The object store is outside that transaction.",
      "The API returns the receipt, then enqueues the job. A sweeper covers a crash between commit and enqueue.",
      "The same key and body re-enter intake and receive the existing receipt. They are not sent directly to the queue."
    ],
    "nodes": [
      {
        "id": "col",
        "label": "Collector",
        "sub": "Holds bytes until receipt",
        "kind": "service",
        "col": 0,
        "row": 0
      },
      {
        "id": "api",
        "label": "Intake API",
        "sub": "Validates and returns the receipt",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "obj",
        "label": "Evidence store",
        "sub": "Put-if-absent before the commit",
        "kind": "storage",
        "col": 3,
        "row": 0
      },
      {
        "id": "db",
        "label": "Resolver database",
        "sub": "Capture, receipt, and job row",
        "kind": "db",
        "col": 2,
        "row": 1
      },
      {
        "id": "q",
        "label": "Job queue",
        "sub": "Reference only, after commit",
        "kind": "queue",
        "col": 2,
        "row": 2
      },
      {
        "id": "again",
        "label": "Same submission",
        "sub": "Same key and body",
        "kind": "state",
        "col": 0,
        "row": 1
      },
      {
        "id": "rcpt",
        "label": "Existing receipt",
        "sub": "No second enqueue",
        "kind": "state",
        "col": 1,
        "row": 2
      }
    ],
    "edges": [
      {
        "from": "col",
        "to": "api",
        "kind": "sync",
        "label": "declare"
      },
      {
        "from": "api",
        "to": "obj",
        "kind": "storage",
        "label": "put first"
      },
      {
        "from": "api",
        "to": "db",
        "kind": "tx",
        "label": "then commit"
      },
      {
        "from": "db",
        "to": "q",
        "kind": "async",
        "label": "enqueue after"
      },
      {
        "from": "again",
        "to": "api",
        "kind": "sync",
        "label": "via intake"
      },
      {
        "from": "api",
        "to": "rcpt",
        "kind": "sync",
        "label": "same receipt"
      }
    ]
  },
  {
    "id": "fig-extraction",
    "question": "Who may propose, advise, or decide?",
    "takeaway": "The deterministic parser writes claims when it supports the artifact. The extractor model only proposes. The reviewer model only advises. A person decides an uncertain identity.",
    "status": "Proposed actor permissions (ADR-006). Any added model cost must fit the proposed USD 40 operating envelope and leave the USD 10 reserve untouched.",
    "text": [
      "A stored capture becomes a parse job.",
      "If an adapter supports the source, it emits claims and normalization continues.",
      "If no adapter supports it, or the adapter fails, the extractor model may propose located claims.",
      "A confident uncontested match can be accepted by rule. A conflict or a new identity opens a review case.",
      "The advisory model attaches a recommendation. An authorized rule or a human decision accepts. Models only propose or advise."
    ],
    "nodes": [
      {
        "id": "cap",
        "label": "Stored capture",
        "sub": "Evidence plus receipt",
        "kind": "state",
        "col": 0,
        "row": 0
      },
      {
        "id": "q",
        "label": "Adapter available?",
        "sub": "supports() on pa_*",
        "kind": "decision",
        "col": 0,
        "row": 1
      },
      {
        "id": "det",
        "label": "Deterministic parser",
        "sub": "Versioned adapter",
        "kind": "service",
        "col": 0,
        "row": 2
      },
      {
        "id": "llm",
        "label": "LLM extractor",
        "sub": "Proposes claims only",
        "kind": "model",
        "col": 1,
        "row": 2
      },
      {
        "id": "norm",
        "label": "Normalize and match",
        "sub": "Profiles, then identity rules",
        "kind": "service",
        "col": 0,
        "row": 3
      },
      {
        "id": "case",
        "label": "Review case",
        "sub": "Durable until a decision",
        "kind": "db",
        "col": 1,
        "row": 3
      },
      {
        "id": "acc",
        "label": "Accepted by rule",
        "sub": "Facts and one outbox row",
        "kind": "db",
        "col": 0,
        "row": 4
      },
      {
        "id": "adv",
        "label": "Advisory reviewer",
        "sub": "Cannot accept or publish",
        "kind": "model",
        "col": 1,
        "row": 4
      },
      {
        "id": "hum",
        "label": "Human reviewer",
        "sub": "Approve, correct, or reject",
        "kind": "human",
        "col": 1,
        "row": 5
      },
      {
        "id": "acc2",
        "label": "Accepted by decision",
        "sub": "Same transaction shape",
        "kind": "db",
        "col": 1,
        "row": 6
      }
    ],
    "edges": [
      {
        "from": "cap",
        "to": "q",
        "kind": "async",
        "label": "job"
      },
      {
        "from": "q",
        "to": "det",
        "kind": "sync",
        "label": "yes"
      },
      {
        "from": "q",
        "to": "llm",
        "kind": "async",
        "label": "no or failed"
      },
      {
        "from": "det",
        "to": "norm",
        "kind": "sync",
        "label": "claims"
      },
      {
        "from": "norm",
        "to": "case",
        "kind": "sync",
        "label": "conflict"
      },
      {
        "from": "norm",
        "to": "acc",
        "kind": "tx",
        "label": "rule match"
      },
      {
        "from": "llm",
        "to": "case",
        "kind": "sync",
        "label": "proposal"
      },
      {
        "from": "case",
        "to": "adv",
        "kind": "async",
        "label": "advice"
      },
      {
        "from": "adv",
        "to": "hum",
        "kind": "sync",
        "label": "recommendation"
      },
      {
        "from": "hum",
        "to": "acc2",
        "kind": "tx",
        "label": "decision"
      }
    ]
  },
  {
    "id": "fig-states",
    "question": "When is a change accepted, applied, and public?",
    "takeaway": "Private application of an item or mapping event is not publication. evt_0142 corrects a listing mapping. Readers see that change only after a separate release is applied.",
    "status": "Proposed (AW-01, AW-03, AW-04, ADR-003, ADR-009). Transaction behavior is still to be proven by SPK-01 and SPK-02.",
    "text": [
      "Accepted item and mapping events commit in the resolver. evt_0142 is listing_mapping.corrected.",
      "The product service may apply those events and still show the previous release.",
      "Acknowledging the mapping event does not authorize a public page.",
      "A separate release command is applied before a reader can see the correction."
    ],
    "nodes": [
      {
        "id": "acc",
        "label": "Accepted privately",
        "sub": "Item and mapping events commit",
        "kind": "db",
        "col": 0,
        "row": 0
      },
      {
        "id": "app",
        "label": "Apply mapping privately",
        "sub": "evt_0142 is not a release",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "rel",
        "label": "Authorized release",
        "sub": "Separate command and event",
        "kind": "gate",
        "col": 2,
        "row": 0
      },
      {
        "id": "pub",
        "label": "Apply the release",
        "sub": "Readers can see this",
        "kind": "service",
        "col": 3,
        "row": 0
      },
      {
        "id": "priv",
        "label": "Still private",
        "sub": "Accepted is not a release",
        "kind": "state",
        "col": 0,
        "row": 1
      },
      {
        "id": "wait",
        "label": "Not readable yet",
        "sub": "Mapping apply is not public",
        "kind": "state",
        "col": 1,
        "row": 1
      }
    ],
    "edges": [
      {
        "from": "acc",
        "to": "app",
        "kind": "async",
        "label": "private events"
      },
      {
        "from": "app",
        "to": "rel",
        "kind": "deny",
        "label": "not this event"
      },
      {
        "from": "rel",
        "to": "pub",
        "kind": "async",
        "label": "release event"
      },
      {
        "from": "acc",
        "to": "priv",
        "kind": "deny",
        "label": "no public read"
      },
      {
        "from": "app",
        "to": "wait",
        "kind": "deny",
        "label": "not a release"
      }
    ]
  },
  {
    "id": "fig-identity",
    "question": "How do a merchant offer, an exact variant, and a pack relate?",
    "takeaway": "A listing is one seller’s offer at its original URL. It maps to one exact variant or one pack. Two 250 g packs are not the 500 g item. Inventory belongs to the listing, not to the variant as a whole.",
    "status": "Established domain model in the design. Geography handling is proposed. All names below are fictional fixtures.",
    "text": [
      "Corvane makes the Strata brand. The Strata P5 family has more than one exact item.",
      "The 1 TB heatsink item is itm_ssd_p5_1t_hs. A listing URL is not that identity.",
      "Pantry & Pour’s Ridgeline 500 g item and the 2 × 250 g pack stay distinct. The merge stays blocked.",
      "Halden sells through more than one source. Each source listing keeps its own URL and observations.",
      "An inventory observation is scoped to a listing and a stock pool at an observed time."
    ],
    "nodes": [
      {
        "id": "mfr",
        "label": "Manufacturer",
        "sub": "mfr_corvane, fictional",
        "kind": "external",
        "col": 0,
        "row": 0
      },
      {
        "id": "brand",
        "label": "Brand",
        "sub": "brd_strata",
        "kind": "state",
        "col": 1,
        "row": 0
      },
      {
        "id": "fam",
        "label": "Product family",
        "sub": "fam_strata_p5",
        "kind": "state",
        "col": 2,
        "row": 0
      },
      {
        "id": "var",
        "label": "Exact variant",
        "sub": "itm_ssd_p5_1t_hs",
        "kind": "state",
        "col": 3,
        "row": 0
      },
      {
        "id": "pack",
        "label": "Pack",
        "sub": "itm_ridgeline_2x250g ≠ 500 g",
        "kind": "state",
        "col": 4,
        "row": 0
      },
      {
        "id": "mer",
        "label": "Merchant",
        "sub": "mer_halden sells",
        "kind": "external",
        "col": 1,
        "row": 1
      },
      {
        "id": "src",
        "label": "Source",
        "sub": "src_lumen_web, src_halden_us",
        "kind": "external",
        "col": 2,
        "row": 1
      },
      {
        "id": "lst",
        "label": "Listing",
        "sub": "lst_lum_p5_1t_hs at its URL",
        "kind": "state",
        "col": 3,
        "row": 1
      },
      {
        "id": "inv",
        "label": "Scoped inventory",
        "sub": "One pool, one observed_at",
        "kind": "state",
        "col": 3,
        "row": 2
      }
    ],
    "edges": [
      {
        "from": "mfr",
        "to": "brand",
        "kind": "sync",
        "label": "owns"
      },
      {
        "from": "brand",
        "to": "fam",
        "kind": "sync",
        "label": "names"
      },
      {
        "from": "fam",
        "to": "var",
        "kind": "sync",
        "label": "exact item"
      },
      {
        "from": "var",
        "to": "pack",
        "kind": "deny",
        "label": "not the same"
      },
      {
        "from": "mer",
        "to": "src",
        "kind": "sync",
        "label": "sells via"
      },
      {
        "from": "src",
        "to": "lst",
        "kind": "sync",
        "label": "offers"
      },
      {
        "from": "lst",
        "to": "var",
        "kind": "sync",
        "label": "maps to"
      },
      {
        "from": "lst",
        "to": "inv",
        "kind": "storage",
        "label": "observed"
      }
    ]
  },
  {
    "id": "fig-shipping",
    "question": "Why can a shipping answer stay unknown?",
    "takeaway": "Missing policy is unknown, never a guess. Stale Northfield evidence is kept and warned, and it stays out of a definitive ranking. Halden to Nevada without a postcode leaves eligibility, free shipping and cost unknown. Halden DE can be eligible while free-shipping terms and cost stay unknown.",
    "status": "Proposed evaluator. The three outcomes were checked against fictional fixtures. No live carrier rating is claimed.",
    "text": [
      "The destination is the buyer’s region, not the collector’s execution region.",
      "No applicable policy produces an unknown answer.",
      "Northfield’s stale policy retains a warned answer.",
      "Halden US to Nevada without a postcode does not invent eligibility.",
      "Halden DE shows that eligibility, free-shipping qualification and cost are separate answers."
    ],
    "nodes": [
      {
        "id": "in",
        "label": "Listing + destination",
        "sub": "Buyer region is an input",
        "kind": "state",
        "col": 1,
        "row": 0
      },
      {
        "id": "pol",
        "label": "Policy on file?",
        "sub": "Scoped to source and merchant",
        "kind": "decision",
        "col": 1,
        "row": 1
      },
      {
        "id": "miss",
        "label": "Unknown",
        "sub": "No policy. Never guessed.",
        "kind": "state",
        "col": 0,
        "row": 2
      },
      {
        "id": "stale",
        "label": "Northfield stale",
        "sub": "Retain, warn, do not rank",
        "kind": "state",
        "col": 1,
        "row": 2
      },
      {
        "id": "nv",
        "label": "Nevada, no postcode",
        "sub": "Eligibility, free and cost unknown",
        "kind": "state",
        "col": 2,
        "row": 2
      },
      {
        "id": "de",
        "label": "Halden DE",
        "sub": "Eligible. Free and cost unknown.",
        "kind": "state",
        "col": 3,
        "row": 2
      }
    ],
    "edges": [
      {
        "from": "in",
        "to": "pol",
        "kind": "sync",
        "label": "evaluate"
      },
      {
        "from": "pol",
        "to": "miss",
        "kind": "deny",
        "label": "no policy"
      },
      {
        "from": "pol",
        "to": "stale",
        "kind": "sync",
        "label": "stale"
      },
      {
        "from": "pol",
        "to": "nv",
        "kind": "sync",
        "label": "no postcode"
      },
      {
        "from": "pol",
        "to": "de",
        "kind": "sync",
        "label": "partial terms"
      }
    ]
  },
  {
    "id": "fig-trust",
    "question": "Which credential is allowed to cross which boundary?",
    "takeaway": "A valid credential is not a global pass. Collectors use a bound OAuth client. Operators use an OIDC session. Queue messages carry no secrets. Model output is untrusted.",
    "status": "Proposed authentication. Cryptography, the identity provider and TLS are simulated in the workbench, not verified here.",
    "text": [
      "Collectors present OAuth2 client credentials to intake. The token binds tenant, worker, stable instance and sources.",
      "Operators sign in with OIDC. Mutations require the session and CSRF protection.",
      "Queue bindings authorize the resolver relay and the product consumer. Payloads hold references, not keys.",
      "The extraction worker holds the provider key. The model never receives that key or a tool.",
      "Public reads use TLS and a release allowlist. They do not use a consumer secret."
    ],
    "nodes": [
      {
        "id": "col",
        "label": "Collector",
        "sub": "Per-instance OAuth client",
        "kind": "service",
        "col": 0,
        "row": 0
      },
      {
        "id": "in",
        "label": "Intake API",
        "sub": "Checks scope, then source",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "ops",
        "label": "Operator browser",
        "sub": "OIDC session and CSRF",
        "kind": "human",
        "col": 0,
        "row": 1
      },
      {
        "id": "admin",
        "label": "Admin API",
        "sub": "Tenant role grants",
        "kind": "service",
        "col": 1,
        "row": 1
      },
      {
        "id": "q",
        "label": "Queues",
        "sub": "Bindings, no secret inside",
        "kind": "queue",
        "col": 2,
        "row": 0
      },
      {
        "id": "model",
        "label": "Model provider",
        "sub": "Output is untrusted",
        "kind": "model",
        "col": 0,
        "row": 3
      },
      {
        "id": "ext",
        "label": "Extraction worker",
        "sub": "Holds the provider key",
        "kind": "service",
        "col": 1,
        "row": 3
      },
      {
        "id": "reader",
        "label": "Public reader",
        "sub": "No consumer secret",
        "kind": "human",
        "col": 0,
        "row": 4
      },
      {
        "id": "api",
        "label": "Product API",
        "sub": "Release allowlist",
        "kind": "service",
        "col": 1,
        "row": 4
      }
    ],
    "edges": [
      {
        "from": "col",
        "to": "in",
        "kind": "sync",
        "label": "OAuth JWT"
      },
      {
        "from": "ops",
        "to": "admin",
        "kind": "sync",
        "label": "OIDC + CSRF"
      },
      {
        "from": "in",
        "to": "q",
        "kind": "async",
        "label": "refs only"
      },
      {
        "from": "ext",
        "to": "model",
        "kind": "sync",
        "label": "API key"
      },
      {
        "from": "reader",
        "to": "api",
        "kind": "sync",
        "label": "TLS read"
      }
    ]
  },
  {
    "id": "fig-events",
    "question": "What happens when an accepted event arrives twice, late, or before its dependency?",
    "takeaway": "Every delivery is checked in the inbox. A duplicate or older revision is ignored, a missing dependency is parked, and a ready revision is applied. Acknowledging a parked event does not close the gap.",
    "status": "Proposed delivery rules (ADR-009 and the queue-continuity text). SPK-02 has not been run.",
    "text": [
      "A decision commits facts and outbox rows under the stream cursor lock.",
      "The relay publishes the committed row. The product inbox checks every delivery.",
      "A duplicate or an older revision is ignored after that check.",
      "A missing dependency is parked and may be acknowledged. The acknowledgment does not close the application gap.",
      "A ready revision is applied in the same transaction as its inbox row, and the queue is acknowledged after that commit."
    ],
    "nodes": [
      {
        "id": "dec",
        "label": "Decision",
        "sub": "dec_0141 is one example",
        "kind": "human",
        "col": 0,
        "row": 0
      },
      {
        "id": "tx",
        "label": "Resolver transaction",
        "sub": "Facts, cursor, outbox",
        "kind": "db",
        "col": 1,
        "row": 0
      },
      {
        "id": "relay",
        "label": "Outbox relay",
        "sub": "Publishes after commit",
        "kind": "service",
        "col": 2,
        "row": 0
      },
      {
        "id": "q",
        "label": "Ingestion queue",
        "sub": "At least once",
        "kind": "queue",
        "col": 3,
        "row": 0
      },
      {
        "id": "in",
        "label": "Product inbox",
        "sub": "Checks every delivery",
        "kind": "service",
        "col": 4,
        "row": 1
      },
      {
        "id": "dup",
        "label": "Ignore",
        "sub": "Duplicate or older revision",
        "kind": "state",
        "col": 3,
        "row": 2
      },
      {
        "id": "park",
        "label": "Park",
        "sub": "Ack does not close the gap",
        "kind": "state",
        "col": 4,
        "row": 2
      },
      {
        "id": "ok",
        "label": "Apply",
        "sub": "Ready, then acknowledge",
        "kind": "db",
        "col": 5,
        "row": 2
      }
    ],
    "edges": [
      {
        "from": "dec",
        "to": "tx",
        "kind": "tx",
        "label": "commit"
      },
      {
        "from": "tx",
        "to": "relay",
        "kind": "sync",
        "label": "row"
      },
      {
        "from": "relay",
        "to": "q",
        "kind": "async",
        "label": "publish"
      },
      {
        "from": "q",
        "to": "in",
        "kind": "async",
        "label": "deliver"
      },
      {
        "from": "in",
        "to": "dup",
        "kind": "sync",
        "label": "duplicate"
      },
      {
        "from": "in",
        "to": "park",
        "kind": "sync",
        "label": "missing dependency"
      },
      {
        "from": "in",
        "to": "ok",
        "kind": "tx",
        "label": "ready"
      }
    ]
  },
  {
    "id": "fig-redelivery",
    "question": "Does a redelivered job get permission to spend again?",
    "takeaway": "A queue delivery is not a paid attempt. An uncertain timeout keeps its reservation. Another paid call needs fresh admission. A rejected admission defers the work.",
    "status": "Proposed correction to the 0.4.0 simulation. Budget controls also propose at most three queue deliveries per job. The interface note proposes a dead letter after five. Those two limits are not reconciled.",
    "text": [
      "Each delivery increments the delivery count, not the paid-attempt count.",
      "A settled attempt id returns the stored result and does not call the provider.",
      "A timeout keeps the reservation. That open liability is not itself a deferral.",
      "Another paid call requires a fresh admission.",
      "Rejected admission defers the work and keeps the original observation."
    ],
    "nodes": [
      {
        "id": "job",
        "label": "Job delivered",
        "sub": "Delivery count increases",
        "kind": "queue",
        "col": 1,
        "row": 0
      },
      {
        "id": "q",
        "label": "Attempt on record?",
        "sub": "Idempotent attempt id",
        "kind": "decision",
        "col": 1,
        "row": 1
      },
      {
        "id": "reuse",
        "label": "Reuse result",
        "sub": "No new provider call",
        "kind": "state",
        "col": 0,
        "row": 2
      },
      {
        "id": "keep",
        "label": "Keep liability",
        "sub": "Timeout stays reserved",
        "kind": "state",
        "col": 2,
        "row": 2
      },
      {
        "id": "adm",
        "label": "Fresh admission",
        "sub": "Required for another paid call",
        "kind": "gate",
        "col": 1,
        "row": 3
      },
      {
        "id": "defer",
        "label": "Defer",
        "sub": "Rejected admission. Data kept.",
        "kind": "state",
        "col": 0,
        "row": 4
      },
      {
        "id": "call",
        "label": "Paid attempt",
        "sub": "Fetch, browser, or model",
        "kind": "external",
        "col": 2,
        "row": 4
      }
    ],
    "edges": [
      {
        "from": "job",
        "to": "q",
        "kind": "async",
        "label": "deliver"
      },
      {
        "from": "q",
        "to": "reuse",
        "kind": "sync",
        "label": "settled"
      },
      {
        "from": "q",
        "to": "keep",
        "kind": "sync",
        "label": "uncertain"
      },
      {
        "from": "q",
        "to": "adm",
        "kind": "sync",
        "label": "another call"
      },
      {
        "from": "adm",
        "to": "defer",
        "kind": "deny",
        "label": "rejected"
      },
      {
        "from": "adm",
        "to": "call",
        "kind": "sync",
        "label": "admitted"
      }
    ]
  },
  {
    "id": "fig-budget-bands",
    "question": "What happens as proposed spending approaches the confirmed ceiling?",
    "takeaway": "The only confirmed number is USD 50 a month. The proposed bands admit paid work under USD 35, defer discretionary work from USD 35, stop new unreserved work at USD 40, and never admit the last USD 10.",
    "status": "Confirmed: the USD 50 recurring-service ceiling. Proposed: the 35 and 40 thresholds and the USD 10 reserve. Not a provider quote.",
    "text": [
      "Below a proposed USD 35, bounded paid work can be reserved and admitted.",
      "From USD 35, browser, model and replay work waits. Essential capture may still be admitted up to USD 40.",
      "At USD 40, new unreserved paid work stops. Already reserved jobs may finish.",
      "The last USD 10 is an uncertainty reserve for tax, FX, late usage reports and overruns. Admission does not spend it."
    ],
    "nodes": [
      {
        "id": "cap",
        "label": "USD 50 ceiling",
        "sub": "Confirmed recurring services",
        "kind": "confirmed",
        "col": 0,
        "row": 0
      },
      {
        "id": "b1",
        "label": "USD 0–35",
        "sub": "Admit after reserving the quote",
        "kind": "state",
        "col": 0,
        "row": 1
      },
      {
        "id": "b2",
        "label": "USD 35–40",
        "sub": "Discretionary work waits",
        "kind": "state",
        "col": 0,
        "row": 2
      },
      {
        "id": "b3",
        "label": "At USD 40",
        "sub": "No new unreserved paid work",
        "kind": "gate",
        "col": 0,
        "row": 3
      },
      {
        "id": "b4",
        "label": "USD 40–50 reserve",
        "sub": "Never admitted",
        "kind": "state",
        "col": 0,
        "row": 4
      }
    ],
    "edges": [
      {
        "from": "cap",
        "to": "b1",
        "kind": "sync",
        "label": "inside"
      },
      {
        "from": "b1",
        "to": "b2",
        "kind": "sync",
        "label": "exposure rises"
      },
      {
        "from": "b2",
        "to": "b3",
        "kind": "sync",
        "label": "envelope"
      },
      {
        "from": "b3",
        "to": "b4",
        "kind": "deny",
        "label": "not spent"
      }
    ]
  },
  {
    "id": "fig-budget-admit",
    "question": "How is one paid request admitted, deferred, or reconciled?",
    "takeaway": "Exposure is the greater of the monthly forecast and accounted liabilities. A usage report is acceptable at exactly 24 hours. Before admission, exposure must be under USD 40. After a reservation it may equal USD 40.",
    "status": "Proposed gate, simulated in the workbench budget prototype. It does not place a provider order.",
    "text": [
      "Unconfirmed prices, or a usage report older than 24 hours, pause new paid work.",
      "Daily reconciliation replaces a reservation with the actual charge once.",
      "Forecast USD 39 against accounted USD 27 still defers a discretionary extraction.",
      "Exposure USD 39.99 plus a USD 0.01 essential capture may admit to exactly USD 40.",
      "Deferred work keeps accepted observations and their original observed time."
    ],
    "nodes": [
      {
        "id": "req",
        "label": "Paid request",
        "sub": "Browser, model, replay, fetch",
        "kind": "state",
        "col": 1,
        "row": 0
      },
      {
        "id": "fresh",
        "label": "Prices and report fresh?",
        "sub": "Exactly 24 h is allowed",
        "kind": "decision",
        "col": 1,
        "row": 1
      },
      {
        "id": "pause",
        "label": "Pause",
        "sub": "Stale report or unpriced service",
        "kind": "state",
        "col": 0,
        "row": 2
      },
      {
        "id": "before",
        "label": "Exposure before",
        "sub": "Must be under USD 40",
        "kind": "state",
        "col": 1,
        "row": 2
      },
      {
        "id": "after",
        "label": "Exposure after quote",
        "sub": "May equal USD 40",
        "kind": "state",
        "col": 1,
        "row": 3
      },
      {
        "id": "disc",
        "label": "Discretionary at 35?",
        "sub": "Browser, model, replay",
        "kind": "decision",
        "col": 1,
        "row": 4
      },
      {
        "id": "defer",
        "label": "Defer",
        "sub": "Keep observed_at",
        "kind": "state",
        "col": 0,
        "row": 5
      },
      {
        "id": "admit",
        "label": "Admit and reserve",
        "sub": "Failed attempts still count",
        "kind": "gate",
        "col": 1,
        "row": 5
      }
    ],
    "edges": [
      {
        "from": "req",
        "to": "fresh",
        "kind": "sync",
        "label": "ask"
      },
      {
        "from": "fresh",
        "to": "pause",
        "kind": "deny",
        "label": "no"
      },
      {
        "from": "fresh",
        "to": "before",
        "kind": "sync",
        "label": "yes"
      },
      {
        "from": "before",
        "to": "after",
        "kind": "sync",
        "label": "add quote"
      },
      {
        "from": "after",
        "to": "disc",
        "kind": "sync",
        "label": "classify"
      },
      {
        "from": "disc",
        "to": "defer",
        "kind": "deny",
        "label": "wait"
      },
      {
        "from": "disc",
        "to": "admit",
        "kind": "sync",
        "label": "essential or under 35"
      }
    ]
  },
  {
    "id": "fig-replay",
    "question": "How does a parser improve without changing when the source was seen?",
    "takeaway": "Repeated fallbacks become one proposal. A new adapter is replayed against the pinned artifact. The original observed time stays. Replay is not a freshness gain.",
    "status": "Established improvement shape in the design. The Lumen draft imp_0003 is a fictional prototype and is not published to an issue tracker.",
    "text": [
      "Review case rc_0007 and four Lumen fallbacks group into imp_0003.",
      "The draft names the evidence, the expected value and a regression fixture, cap_lum_0310.",
      "The proposed adapter is pa_lumen_specgrid@1.0.0. It is not yet accepted software.",
      "Replay reads the stored bytes and records a before/after comparison.",
      "captured_at and observed_at do not move to the replay time."
    ],
    "nodes": [
      {
        "id": "rc",
        "label": "Reviewed case",
        "sub": "rc_0007 · dec_0141",
        "kind": "human",
        "col": 0,
        "row": 0
      },
      {
        "id": "fb",
        "label": "Repeated fallbacks",
        "sub": "Four fictional Lumen captures",
        "kind": "state",
        "col": 1,
        "row": 0
      },
      {
        "id": "grp",
        "label": "One proposal",
        "sub": "imp_0003",
        "kind": "service",
        "col": 2,
        "row": 0
      },
      {
        "id": "fx",
        "label": "Pinned fixture",
        "sub": "cap_lum_0310 bytes",
        "kind": "storage",
        "col": 3,
        "row": 0
      },
      {
        "id": "fix",
        "label": "Proposed adapter",
        "sub": "pa_lumen_specgrid@1.0.0",
        "kind": "service",
        "col": 2,
        "row": 1
      },
      {
        "id": "rep",
        "label": "Replay",
        "sub": "Same artifact, new version",
        "kind": "service",
        "col": 3,
        "row": 1
      },
      {
        "id": "out",
        "label": "Before and after",
        "sub": "observed_at unchanged",
        "kind": "state",
        "col": 3,
        "row": 2
      }
    ],
    "edges": [
      {
        "from": "rc",
        "to": "fb",
        "kind": "sync",
        "label": "expected"
      },
      {
        "from": "fb",
        "to": "grp",
        "kind": "sync",
        "label": "group"
      },
      {
        "from": "grp",
        "to": "fix",
        "kind": "sync",
        "label": "fix area"
      },
      {
        "from": "fix",
        "to": "rep",
        "kind": "sync",
        "label": "new version"
      },
      {
        "from": "grp",
        "to": "fx",
        "kind": "storage",
        "label": "pin"
      },
      {
        "from": "fx",
        "to": "rep",
        "kind": "async",
        "label": "read bytes"
      },
      {
        "from": "rep",
        "to": "out",
        "kind": "sync",
        "label": "compare"
      }
    ]
  },
  {
    "id": "fig-recovery",
    "question": "How can a projection be rebuilt without rewriting history?",
    "takeaway": "A snapshot is safe only when its rows and committed cursor describe the same history. The shadow rebuild catches up on a separate cursor. Cutover swaps the live pointer. Events from an older history epoch are ignored, not relabeled.",
    "status": "Proposed (ADR-004). SPK-05 is not done. The workbench recovery screen is a simulation.",
    "text": [
      "Live writes lock the cursor, then commit facts and the outbox together.",
      "An operator starts a rebuild. The export pins rows and cursor W under a lease.",
      "The shadow projection loads the snapshot, then applies the retained tail after W.",
      "Shadow progress never advances the live retention cursor.",
      "Cutover swaps the serving generation and the live cursor before the lease is released.",
      "A restored older epoch is fenced. Those events are terminally ignored."
    ],
    "nodes": [
      {
        "id": "live",
        "label": "Live writer",
        "sub": "Lock cursor, then commit",
        "kind": "db",
        "col": 0,
        "row": 0
      },
      {
        "id": "pin",
        "label": "Pinned snapshot",
        "sub": "Rows and cursor W match",
        "kind": "storage",
        "col": 1,
        "row": 0
      },
      {
        "id": "shadow",
        "label": "Shadow projection",
        "sub": "Own cursor and lease",
        "kind": "service",
        "col": 2,
        "row": 0
      },
      {
        "id": "tail",
        "label": "Retained tail",
        "sub": "Ordered events after W",
        "kind": "queue",
        "col": 2,
        "row": 1
      },
      {
        "id": "cut",
        "label": "Cutover",
        "sub": "Swap pointer, then drop lease",
        "kind": "gate",
        "col": 3,
        "row": 0
      },
      {
        "id": "old",
        "label": "Older epoch",
        "sub": "Ignored, never relabeled",
        "kind": "state",
        "col": 3,
        "row": 1
      }
    ],
    "edges": [
      {
        "from": "live",
        "to": "pin",
        "kind": "storage",
        "label": "export"
      },
      {
        "from": "pin",
        "to": "shadow",
        "kind": "sync",
        "label": "load"
      },
      {
        "from": "shadow",
        "to": "tail",
        "kind": "async",
        "label": "catch up"
      },
      {
        "from": "shadow",
        "to": "cut",
        "kind": "tx",
        "label": "swap"
      },
      {
        "from": "cut",
        "to": "old",
        "kind": "deny",
        "label": "fence"
      }
    ]
  },
  {
    "id": "fig-deploy-local",
    "question": "What runs locally before any recurring service is paid?",
    "takeaway": "The first slice is a fictional Pantry & Pour feed on a laptop: Hono on Node, local PostgreSQL, files by checksum, and a fake model. Domain ports stay stable. Adapters are the local ones.",
    "status": "Proposed stack (ADR-007). Not approved and not running. Pantry & Pour does not prove a real source.",
    "text": [
      "A fixture feed submits captures to the resolver process.",
      "ArtifactStore is a filesystem. JobTransport is PostgreSQL. The model port is a fake.",
      "resolver_app writes the resolver schema. product_app cannot.",
      "product_app writes projections. No hosted account is required for this stage."
    ],
    "nodes": [
      {
        "id": "feed",
        "label": "Fixture feed",
        "sub": "Pantry & Pour JSON",
        "kind": "state",
        "col": 0,
        "row": 0
      },
      {
        "id": "api",
        "label": "Resolver on Node",
        "sub": "Hono, proposed",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "prod",
        "label": "Product on Node",
        "sub": "Same machine",
        "kind": "service",
        "col": 2,
        "row": 0
      },
      {
        "id": "ports",
        "label": "Stable ports",
        "sub": "Domain code stays here",
        "kind": "gate",
        "col": 1,
        "row": 1
      },
      {
        "id": "fake",
        "label": "Fake model",
        "sub": "No paid call",
        "kind": "model",
        "col": 0,
        "row": 2
      },
      {
        "id": "fs",
        "label": "Filesystem artifacts",
        "sub": "By checksum",
        "kind": "storage",
        "col": 1,
        "row": 2
      },
      {
        "id": "pg",
        "label": "Local PostgreSQL",
        "sub": "Two schemas, two roles",
        "kind": "db",
        "col": 2,
        "row": 2
      }
    ],
    "edges": [
      {
        "from": "feed",
        "to": "api",
        "kind": "sync",
        "label": "capture"
      },
      {
        "from": "api",
        "to": "prod",
        "kind": "async",
        "label": "events"
      },
      {
        "from": "api",
        "to": "ports",
        "kind": "sync",
        "label": "call ports"
      },
      {
        "from": "ports",
        "to": "fake",
        "kind": "sync",
        "label": "model"
      },
      {
        "from": "ports",
        "to": "fs",
        "kind": "storage",
        "label": "bytes"
      },
      {
        "from": "ports",
        "to": "pg",
        "kind": "tx",
        "label": "SQL"
      }
    ]
  },
  {
    "id": "fig-deploy-hosted",
    "question": "What is the proposed hosted arrangement, if a spike allows it?",
    "takeaway": "Ports stay the same. Adapters change to private object storage, queues, Hyperdrive and an optional metered model. Hosted spend is not authorized by this guide.",
    "status": "Proposed (ADR-007, DJ-07). Open if SPK-01 fails: a Node container near the database is the recorded fallback. No account is created here.",
    "text": [
      "Collectors are proposed as Workers plus Cron, with a browser or regional host only if a source needs it.",
      "The resolver and product service are proposed as Hono on Workers.",
      "Evidence moves to a private R2 bucket. Jobs move to queues. PostgreSQL is reached through Hyperdrive.",
      "The model adapter is optional and metered. It is off until admission allows it.",
      "If the Worker spike fails, the domain packages stay and the process adapter changes."
    ],
    "nodes": [
      {
        "id": "col",
        "label": "Workers collector",
        "sub": "Cron, feed or API",
        "kind": "service",
        "col": 0,
        "row": 0
      },
      {
        "id": "api",
        "label": "Resolver worker",
        "sub": "Adapter may change",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "prod",
        "label": "Product worker",
        "sub": "Separate deployable",
        "kind": "service",
        "col": 2,
        "row": 0
      },
      {
        "id": "ports",
        "label": "Same ports",
        "sub": "Only adapters change",
        "kind": "gate",
        "col": 1,
        "row": 1
      },
      {
        "id": "model",
        "label": "Model API",
        "sub": "Optional, metered",
        "kind": "external",
        "col": 0,
        "row": 2
      },
      {
        "id": "r2",
        "label": "Private R2",
        "sub": "S3-compatible assumption",
        "kind": "storage",
        "col": 1,
        "row": 2
      },
      {
        "id": "pg",
        "label": "Hyperdrive + PostgreSQL",
        "sub": "Provider not chosen",
        "kind": "db",
        "col": 2,
        "row": 2
      }
    ],
    "edges": [
      {
        "from": "col",
        "to": "api",
        "kind": "sync",
        "label": "capture"
      },
      {
        "from": "api",
        "to": "prod",
        "kind": "async",
        "label": "queue events"
      },
      {
        "from": "api",
        "to": "ports",
        "kind": "sync",
        "label": "call ports"
      },
      {
        "from": "ports",
        "to": "model",
        "kind": "sync",
        "label": "if admitted"
      },
      {
        "from": "ports",
        "to": "r2",
        "kind": "storage",
        "label": "bytes"
      },
      {
        "from": "ports",
        "to": "pg",
        "kind": "tx",
        "label": "SQL"
      }
    ]
  },
  {
    "id": "fig-milestones",
    "question": "What is the proposed order of build, measurement, and decision?",
    "takeaway": "Local milestones spend no recurring service money. Hosted work starts only after prices fit the confirmed ceiling. A second source is a later decision, not a launch promise.",
    "status": "Unapproved plan. The 0.4.0 architecture baseline still stands. DJ-11’s two-scraper launch is narrowed by this plan and still awaits Jonathan.",
    "text": [
      "Gate 1 asks for the decisions the plan names before code that assumes them.",
      "M0 and M1 build ports and the Pantry & Pour slice locally.",
      "Gate 2 is pricing. Until it passes, stay local.",
      "M2 runs platform spikes that may bill. M3 watches one real source in private.",
      "M4 publishes only inside an approved policy. M5 decides a second source with the SPK-09 fit rule."
    ],
    "nodes": [
      {
        "id": "g1",
        "label": "Gate 1",
        "sub": "Decisions before the slice",
        "kind": "gate",
        "col": 0,
        "row": 0
      },
      {
        "id": "m0",
        "label": "M0 skeleton",
        "sub": "Ports, schemas, ledger",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "m1",
        "label": "M1 fixture slice",
        "sub": "Pantry & Pour, no fee",
        "kind": "service",
        "col": 2,
        "row": 0
      },
      {
        "id": "g2",
        "label": "Gate 2 pricing",
        "sub": "Must fit USD 50",
        "kind": "gate",
        "col": 3,
        "row": 0
      },
      {
        "id": "m2",
        "label": "M2 spikes",
        "sub": "May bill. SPK-01, 02, 05",
        "kind": "service",
        "col": 3,
        "row": 1
      },
      {
        "id": "m3",
        "label": "M3 shadow source",
        "sub": "Private. No public release.",
        "kind": "service",
        "col": 2,
        "row": 1
      },
      {
        "id": "m4",
        "label": "M4 scoped release",
        "sub": "Only an approved policy",
        "kind": "gate",
        "col": 1,
        "row": 1
      },
      {
        "id": "m5",
        "label": "M5 second source?",
        "sub": "Add, reduce, or defer",
        "kind": "decision",
        "col": 0,
        "row": 1
      }
    ],
    "edges": [
      {
        "from": "g1",
        "to": "m0",
        "kind": "sync",
        "label": "pass"
      },
      {
        "from": "m0",
        "to": "m1",
        "kind": "sync",
        "label": "local"
      },
      {
        "from": "m1",
        "to": "g2",
        "kind": "sync",
        "label": "priced?"
      },
      {
        "from": "g2",
        "to": "m2",
        "kind": "sync",
        "label": "pass"
      },
      {
        "from": "m2",
        "to": "m3",
        "kind": "sync",
        "label": "one source"
      },
      {
        "from": "m3",
        "to": "m4",
        "kind": "sync",
        "label": "policy"
      },
      {
        "from": "m4",
        "to": "m5",
        "kind": "sync",
        "label": "fit rule"
      }
    ]
  },
  {
    "id": "fig-measures",
    "question": "Which measurements decide the second source?",
    "takeaway": "Unknowns stay unknown. They are not entered as zero. The proposed fit rule adds a second source only when the two-source forecast is at or under USD 35 and operating exposure stays at or under USD 40, with USD 10 still reserved.",
    "status": "Proposed spikes SPK-07, SPK-08 and SPK-09. Nothing here has been measured.",
    "text": [
      "SPK-07 measures database active hours and storage growth against the assumed 16 hours a day.",
      "SPK-08 measures parser failure, model attempts including retries, and human review minutes.",
      "SPK-09 reruns the cost model with those measurements.",
      "A miss on the fit rule means reduce, drop browser dependence, or defer. It does not silently add the source."
    ],
    "nodes": [
      {
        "id": "m3",
        "label": "Shadow cycles",
        "sub": "One authorized real source",
        "kind": "service",
        "col": 1,
        "row": 0
      },
      {
        "id": "s7",
        "label": "SPK-07 database",
        "sub": "Hours and storage growth",
        "kind": "state",
        "col": 0,
        "row": 1
      },
      {
        "id": "s8",
        "label": "SPK-08 effort",
        "sub": "Parser, model, review time",
        "kind": "state",
        "col": 1,
        "row": 1
      },
      {
        "id": "s9",
        "label": "SPK-09 fit rule",
        "sub": "Forecast ≤ 35 and ≤ 40",
        "kind": "gate",
        "col": 2,
        "row": 1
      },
      {
        "id": "go",
        "label": "Second source decision",
        "sub": "Add, reduce, or defer",
        "kind": "decision",
        "col": 2,
        "row": 2
      }
    ],
    "edges": [
      {
        "from": "m3",
        "to": "s8",
        "kind": "sync",
        "label": "measure"
      },
      {
        "from": "s7",
        "to": "s8",
        "kind": "sync",
        "label": "hours"
      },
      {
        "from": "s8",
        "to": "s9",
        "kind": "sync",
        "label": "together"
      },
      {
        "from": "s9",
        "to": "go",
        "kind": "sync",
        "label": "memo"
      }
    ]
  },
  {
    "id": "fig-enroll",
    "question": "How does a collector instance become allowed to submit?",
    "takeaway": "An operator provisions the client. The instance gets a client-credentials token, then registers. Registration records a lease. It does not start a machine or change the tenant.",
    "status": "Proposed enrollment (collector registration). Cryptography, the identity provider, and TLS are simulated in the workbench, not verified here.",
    "text": [
      "An operator provisions a per-instance client before any token is issued.",
      "The collector requests an OAuth2 client-credentials token from the token service.",
      "The token carries the stable instance, the worker, the tenant, and the source grants.",
      "POST /v1/workers/register upserts the instance and returns a lease.",
      "A later heartbeat renews that lease. Registration itself never starts a machine."
    ],
    "nodes": [
      {
        "id": "op",
        "label": "Operator provisions",
        "sub": "Client, worker, sources",
        "kind": "human",
        "col": 0,
        "row": 0
      },
      {
        "id": "ask",
        "label": "Collector asks",
        "sub": "Client credentials",
        "kind": "service",
        "col": 0,
        "row": 1
      },
      {
        "id": "idp",
        "label": "Token service",
        "sub": "Choice of provider is open",
        "kind": "external",
        "col": 0,
        "row": 2
      },
      {
        "id": "reg",
        "label": "Register",
        "sub": "Bearer token, one instance",
        "kind": "service",
        "col": 0,
        "row": 3
      },
      {
        "id": "lease",
        "label": "Lease recorded",
        "sub": "Same body returns the lease",
        "kind": "db",
        "col": 0,
        "row": 4
      },
      {
        "id": "beat",
        "label": "Heartbeat later",
        "sub": "Renews. Does not grant more.",
        "kind": "service",
        "col": 0,
        "row": 5
      }
    ],
    "edges": [
      {
        "from": "op",
        "to": "ask",
        "kind": "sync",
        "label": "provision"
      },
      {
        "from": "ask",
        "to": "idp",
        "kind": "sync",
        "label": "token"
      },
      {
        "from": "idp",
        "to": "reg",
        "kind": "sync",
        "label": "bearer"
      },
      {
        "from": "reg",
        "to": "lease",
        "kind": "tx",
        "label": "upsert"
      },
      {
        "from": "lease",
        "to": "beat",
        "kind": "sync",
        "label": "renew"
      }
    ]
  },
  {
    "id": "fig-replay-seq",
    "question": "What is handed off when an operator asks for a replay?",
    "takeaway": "Dry run is the default. The API writes one queue message per capture. Each message pins the parser and keeps the original observed time. Apply mode waits for a reviewer.",
    "status": "Proposed replay job. The Lumen improvement imp_0003 is a fictional draft. This is the message sequence, not a completed replay.",
    "text": [
      "The operator calls the admin API. Dry run is the default.",
      "Apply mode is refused unless a reviewer has approved the improvement.",
      "The API fans out one resolution.replay.v1 message per capture.",
      "The consumer reads the pinned artifact and does not fetch the shop again.",
      "observed_at stays on the original capture. New claims still need the accept path."
    ],
    "nodes": [
      {
        "id": "op",
        "label": "Operator request",
        "sub": "Dry run unless approved",
        "kind": "human",
        "col": 0,
        "row": 0
      },
      {
        "id": "api",
        "label": "Admin API",
        "sub": "Checks pins and tenant",
        "kind": "service",
        "col": 0,
        "row": 1
      },
      {
        "id": "q",
        "label": "One message each",
        "sub": "replay id and capture id",
        "kind": "queue",
        "col": 0,
        "row": 2
      },
      {
        "id": "bytes",
        "label": "Pinned artifact",
        "sub": "Stored bytes, not a new fetch",
        "kind": "storage",
        "col": 0,
        "row": 3
      },
      {
        "id": "cmp",
        "label": "Compare claims",
        "sub": "observed time unchanged",
        "kind": "state",
        "col": 0,
        "row": 4
      },
      {
        "id": "hold",
        "label": "Apply waits",
        "sub": "Reviewer approval required",
        "kind": "gate",
        "col": 0,
        "row": 5
      }
    ],
    "edges": [
      {
        "from": "op",
        "to": "api",
        "kind": "sync",
        "label": "request"
      },
      {
        "from": "api",
        "to": "q",
        "kind": "async",
        "label": "fan out"
      },
      {
        "from": "q",
        "to": "bytes",
        "kind": "storage",
        "label": "read"
      },
      {
        "from": "bytes",
        "to": "cmp",
        "kind": "sync",
        "label": "compare"
      },
      {
        "from": "cmp",
        "to": "hold",
        "kind": "deny",
        "label": "dry run"
      }
    ]
  }
]
