One platform, two ways in. The science lives in the engine and library; shared product logic lives once in services; Fox (people) and Machines (AI agents) are thin surfaces on top. The work is nine numbered workstreams; each item is a ticket in the Cortex Delivery board. The goal running through all of it: thinner surfaces, a fatter shared middle, every capability built once.
Waiting on you live · labelled needs:david
Sequence & dependencies
live lanes
9/11 done
10/19 done
starts once Surveys reaches its executor (3.5)
Build alongside the spine; only the job-store swap (2.3) waits (for 1.3 + 3.5).
Nothing blocks these; they run whenever there is a slot.
Computed from the tickets this run — every item currently Ready, In Progress or In Review.
7.3 analysis blocks · 8.2 v6.1 · 8.6/8.7 drivers + Likert — need Matt's rulings (8.4).
Gantt · effort & sequence T-shirt sizes · live
The work — by workstream active shown · done in Landed
Objective: Stand up the guardrails and coordination so the build runs safely, in parallel, without rotting.
Needs: Unlocks the whole critical path. These are live lanes, not board tickets.
Objective: Give the platform a real memory — one production database for accounts, keys, assets and results.
Needs: Store ruled (D-021, production). 2.3 waits on 1.3 + 3.5. · epic #647
staff_role → unlocks the 2.7 admin write-UI (RT-J #7 prerequisite).Objective: Prove the build-once model — move the first real feature into shared services, thin on both surfaces.
Needs: Needs the 1.1 gate + 1.3 job seam; not the data platform. Spec 3.1 ruled. · epic #648
app/studies/_shared/selection.py or similar) that owns audience evaluation (apply_filter over the profile photograph, already in app/audiences/count.py) and participant sampling (the client's stratifiedSample design and seed, already ported byte for byte in app/survey/sampler.py), exposed so every study runner selects its members on the service from a population pin plus a filter spec plus a seed, instead of receiving member_refs chosen in the browser. The Fox adapters stop sending member lists; the client's resolveParticipants and applyFilter become display-only; population.json shrinks to what the screens draw. Behind a flag with the current wire as the oracle.studies/_shared/jsnum.py, each annotated with the client line it mirrors and the re-baseline that retires it; #947's register lists them.select_participants and stamp_provenance as shared functions that each executor calls in place of reading client-chosen member_refs; each executor keeps its own order of operations. The pipeline (the study kernel) is not this ticket: it is filed at re-baseline R2 when the goldens are re-cut anyway.POST /v1/studies/{type}/runs taking a population pin, an audience definition, a study configuration and parameters, served by the same executors; the v1 job kinds untouched until each client runner retires (#931); the Machines tool (#613) can use v2.Objective: Let an AI agent commission a survey end to end — the first Machines proof of concept, internal only.
Needs: Branches off the spine once Surveys reaches its executor (3.5). Delta ruled (D-022). · epic #649
Objective: Close the honesty and cleanup gaps in the Fox app so demos and pilots are trustworthy.
Needs: No gate — runs anytime, full-auto to verified PRs. · epic #650
rebuild/scripts/) that scans every screen under rebuild/src/screens/** and ds-candidates/** for numeric rendering (toFixed, fmtPercent, Math.round, % formatting, chart values) and writes a generated docs/rebuild/NUMBERS_REGISTER.md: one row per rendered number with its source class: **service-computed** (a contract-typed result field), **client-formatted from service data**, **client-computed from client data**, **constant**, or **baked**. The last three are debt and must carry a ticket number in the register. A lint flags the obvious client-computed sources. No UI is built (David, 2026-09-05: no provenance badge; provenance travels in the service receipts, which Machines receives too).04-sales-demo/david/workspace/infra/fox/FEATURE_KICKOFF.md recipes **D (a new engine call)** and **E (a new study type)** rewritten from the six runner ports as they were actually done: a service executor with goldens (app/studies/<kind>/, changes/ fragment, TEST_CATALOGUE.md family), a compat async alias, a Fox adapter behind an <kind>_execution flag, the allowlist route plus the verify_v5 ratification in 01-bis/nets/test_20_serving_surface.py, catalogue cases (functional plus live-staged), a live-staged parity run, David's flip. Plus a one-page "where logic lives" rule pointing at the disposition registry (#944).engine-photograph files in 04-sales-demo/david/workspace/data/baked/ (bulk_cache.json, subject_profiles.json, competitor_profiles.json, creative_review_camden.json, survey_camden.json, creative_review.json) either regenerated from the live service at the current pin over the new population, with MANIFEST.json provenance updated and the bake_*.py scripts pointed at the service, or removed with the screens that read them shown an honest empty state. The three fixture-content files stay.Objective: Build the shared plumbing both surfaces rely on — the AI gateway, accounts, a real release path.
Needs: 6.1 rides the Surveys path; 6.3 is independent and unlocks retiring the old VM. · epic #651
sample_cap (250) and study_min_sizes from 04-sales-demo/david/workspace/data.json become service-side parameters with defaults in app/config.py, returned to the client through the existing config or status route, and overridable per study submission within a service-enforced ceiling; the service executors read them from one place (app/survey/sampler.py SAMPLE_CAP, sensitivity's 80).03-bis-service/app/gateway/: the task registry grouped into features with a default model per feature; policy resolution platform → feature → task → surface → account; an asynchronous OpenRouter client that sends models fallbacks and provider preferences and reads the answering model, generation id and cost; a global in-flight cap with per-feature allowances and a bounded queue; POST /v1/llm/batch fan-out in input order; one provenance row per call with model_requested, model_reported, provider_response_id, config_hash, version_observable = true. Design: docs/coordination/scopes/621_LLM_GATEWAY_2026-09-05.md §3.survey/voicing.py's voice_quote_for_participant and analyst_read_for_study call the gateway's in-process function instead of provider.complete, with the model pinned (VOICING_MODEL) and the tasks marked pinned in the registry.generate_market_insights, read_news_cultural_snapshot and read_socials_cultural_snapshot registered with class search, model perplexity/sonar, running as llm_task jobs with the poll route (45 s class does not fit the sync request budget); Fox's /api/llm forwards them under the llm_via_gateway flag.survey/executor.py:215) and the interview executor's per-participant calls dispatched through the gateway's batch fan-out with the feature's parallelism, results assembled in input order.TASK_ENDPOINT_CLASS generated from GET /v1/llm/tasks and pinned by a test.POST /v1/llm/ingest/{describe-image|scrape|youtube} on the service; the three Pages functions become forwarders with the server-held key and a typed 503 when unconfigured.generate-image forwards to POST /v1/llm/images (stays app-triggered, loses its private key); genericCompletion removed from functions/api/llm.js; its two callers (legacy-inputs-panel.ts, inputs-flows.ts) get registered tasks.gateway.model_policy (append-only, audit row per change), PUT /v1/admin/llm-policy, and the AC admin console's LLM and costs panel showing the effective policy and its config_hash; the engine domain read-only there.app/context.py: one immutable CallContext (account, project, surface, scopes, billing model, LLM credentials or BYO, flags, request id, engine pin), resolved once per request and once per job, adopted router by router; JobContext unified with it.job_events rows, aggregated per stage (p50, p95 for per-persona loops), read through the jobs API; the box health report includes them.--role api and --role worker; the API serves requests and never runs a study or loads a population beyond the profiles sidecar for the count; one worker claims job rows with a lease on the SQLite store; the fatal box verify checks both containers; in Fox, the async overall budgets (120 s single, 180 s bulk in engineAsync.ts) replaced by poll-until-done with a per-poll timeout and an elapsed indicator.work.jobs on Postgres with SELECT ... FOR UPDATE SKIP LOCKED, N workers configurable, the lease reaper across workers, the SQLite store retired.api_keys.expires_at and a sweeper; a keys:mint scope that can only issue short-lived humans-surface keys; the Pages proxy mints one per workspace session bound to the Access-authenticated email and the claimed active account (recorded as a claim until memberships exist), and attaches it to every engine and study call; keyed twins on /v1 for the m2, m3 and m4 analyse routes and their async forms; a flag per route with rollback to the keyless path; a humans-surface default budget.app/policy.py: limits, budgets, caps and model policy in one in-process, hashed snapshot keyed by surface and account, reloaded on change, built first as a facade over config.py so there is one authority; #960's caps join it; config.py keeps deployment settings only.d1_import.py (2,462 lines, the largest module in the service) moves to a tools/ package the app does not import, with its job kind kept through a thin adapter or removed after the migration; cache_rewarm runs under the worker role only.Objective: Grow the product by pulling each study type into shared services, reusing the Surveys machinery.
Needs: Needs the Surveys machinery (3). 7.3 also needs Matt (8.4). · epic #652
docs/coordination/STUDY_MATHS_DEFECT_REGISTER.md: one row per known defect carried into the service by the port-as-photograph doctrine, harvested from every executor's KNOWN DEFECTS docstring (03-bis-service/app/studies/*/executor.py, app/survey/executor.py, app/audiences/count.py) plus #793 to #796, with: the defect, where it lives, the evidence, whether it is an engineering fix or a Matt science question, and the re-baseline event that retires it (R1 with the repin, R2 after the sitting, or later).results_hash equality (or the STOP) is recorded on #630 as a checklist row with the run date, the service build, the population id and hash.CohortDetailScreen.tsx (and wherever a service count replaces a client estimate), a guard: if the service count contradicts the client's own estimate by a wide margin (for example the service says 0 where the estimate says thousands), the UI shows both numbers with an honest label instead of silently replacing the estimate; Big-Five boundary filters allow a small tolerance (one to two per cent; #902 measured 2651 versus 2652) so the guard does not fire spuriously.rebuild/src/logic/clustering.ts (pickClusterCount, doClustering, autoClusterAudience, backfillMissingClusters) ported to a service job kind (audience_cluster or a step of the selection module) as a photograph: same algorithm, same seed handling, golden-locked to a frozen oracle from the client, with a known_defects list. The Fox screens that cluster (ClusterIntoPersonasDialog, CohortDetailScreen, focus-group and interview configs) call it behind a flag.runners/quadrant.ts (runCompetitorQuadrantStudy, runPositioningQuadrantStudy) and competitor_quadrant.ts (importance ranking, axis scaling, projection, white-space detection) ported to two service job kinds with goldens frozen from the client (the existing quadrant-maths.dualrun.spec.ts and the characterization spec are the oracles), compat aliases, Fox adapters behind flags, catalogue cases, and a known_defects list stating which of the maths is client-invented.Objective: Engine & science — the 100k population, the capability workshop, and Matt's handover items.
Needs: 8.1 is urgent and runs now; most rows are Matt-gated. · epic #653
data/population.json regenerated from the subsample's rows (same rows as the service's profile photograph), HOSTED_POPULATION_ID, HOSTED_POPULATION_HASH and HOSTED_POPULATION_VERSION in rebuild/src/logic/engine.ts moved, getAudiencePopulationId() in population.ts moved, the three hard-coded 10,000 denominators found in SAMPLING_100K_OPTIONS_2026-09-04.md made data-driven, the frozen-file re-baseline (--write-baseline with David's own intent sentence), and the catalogue cases that pin known answers (TC-AUD-26's 5,053) re-pinned deliberately.uk_100k_v6 (a one-off seeded draw raked to the census marginals or the blessed marginal design, verified against the 100k with spike S2's protocol; never stratified_persona_indices, never even-stride); (2) the Σ_pop rebuild against the subsample with a condition-number assert as a gate (Matt: the last wrong Σ_pop was near-singular at 4.8e7 against 45); (3) the favourability-head build script, which is not in the repo (question 4 on the Matt list).bislib/engine/m4/narratives.py (_call_anthropic), moved out: the service owns narrative generation through its provider seam (the compat-M2 provider and budgets), the library's M4 becomes fully deterministic, and the facade signature drops the narrative side effect. Segment narratives keep their current wording via the same prompts, now on the service.docs/coordination/BRIEF_MATT_SAMPLED_VECTORISED_SERVING_2026-09-04.md: the data layout and the array contract (memory-mapped population, the statistics artefacts, how a sample is addressed), the sampler specification, the parity and tolerance policy with the σ protocol as its first step, the engine-to-service contract, the migration path and re-baseline plan, effort and sequencing, and the split between what Matt builds and what we build. Slice 1 is the array-in facade (react and analyse_population taking arrays, not List[HSV]).02-bis-lib/tests/parity_v5/evidence/ with the numbers.app/populations/ owning identity (id, hash, version), storage (curated, bespoke, subsample), the profiles photograph and the caps; registry.py, bespoke_registry.py, the resolver and records, and the profiles build in audiences/count.py fold in; cache_rewarm's keyset providers use it.Objective: Security baseline, dependency debt, and pre-customer licensing validation.
Needs: 9.1 sweep can start now (Codex); 9.2 has David's go. · epic #654
service status check (workflow service-ci, job name service) added to ruleset 18742520's required checks, and .github/protection-baseline.json updated to match so protection-drift stays green. Consider verify_v5 (bis-v5-verify) in the same change.Objective: Operational, board and CI/bookkeeping tickets — outside the 1-9 delivery spine.
Needs: No delivery dependency; kept homed for the audit trail, not a workstream of work.