From aa7fb5e8df60b68227e23cac395d4d677149a81a Mon Sep 17 00:00:00 2001
From: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
Date: Fri, 23 Oct 2026 14:32:42 -0700
Subject: [PATCH 1/5] fix(ingestion): replace synthetic recall drill with
honest empty result
Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
---
services/ingestion/app/proof_recall_router.py | 169 ++++--------------
.../tests/test_proof_recall_router.py | 58 +++---
2 files changed, 69 insertions(+), 158 deletions(-)
diff --git a/services/ingestion/app/proof_recall_router.py b/services/ingestion/app/proof_recall_router.py
index aec0d4c26..d25a26e53 100644
--- a/services/ingestion/app/proof_recall_router.py
+++ b/services/ingestion/app/proof_recall_router.py
@@ -1,48 +1,32 @@
"""Public recall-drill endpoint.
-Why this belongs in the FSMA pipeline (production-surface promotion rule
-per ``PRODUCTION_SURFACE.md``):
-
-The core production contract is::
-
- ingest -> canonicalize -> validate -> persist evidence -> export
-
-This router adds a public, tenantless surface that demonstrates the
-*recall-response* capability built on top of the export stage. It is
-the FSMA pipeline's public proof surface for recall capability: a
-prospect, regulator, or trade partner can run a deterministic recall
-drill against a frozen synthetic dataset and observe the wall-clock
-trace time vs. the FDA 24-hour mandate, without an account, without
-tenant context, and without reading any tenant data.
-
Endpoint: ``POST /api/v1/proof/recall-drill``
-- No auth (public). Rate limit: 5 requests per minute per IP via the
- shared slowapi ``limiter`` (``shared.rate_limit``).
-- Operates *only* against a frozen synthetic dataset baked into this
- module. The endpoint MUST NEVER read real tenant data: static
- analysis of this module should show zero imports of
- ``shared.database``, ``CTEPersistence``, or any tenant-scoped
- client.
-- Output is deterministic — every call returns the same step list,
- ``time_to_trace_seconds`` (sum of step durations), ``lot_code``,
- and ``output_csv_url``.
-- The pre-generated recall CSV referenced by ``output_csv_url`` lives
- in the frontend's public assets (``/samples/sample_recall_drill.csv``)
- and was produced from the synthetic 7-CTE dataset.
+This is a public, tenantless, unauthenticated surface (rate limit: 5
+requests per minute per IP via the shared slowapi ``limiter``). It MUST
+NEVER read tenant data: this module imports no ``shared.database``,
+``CTEPersistence`` or other tenant-scoped client.
+
+Because it cannot read tenant data, it has no real recall to trace. It
+previously returned a frozen synthetic drill (an invented lot, invented
+facilities, invented step durations and a pre-generated sample CSV).
+That was fake data presented as a proof, so it was removed. The endpoint
+now returns an honest empty result: no steps, no lot code, no trace time
+and no CSV, with ``data_available: false``. The only populated value is
+the FDA 24-hour records-production window from 21 CFR 1.1455(c)(3),
+which is a regulatory constant, not data.
Returned JSON shape::
{
- "steps": [
- {"name": str, "duration_ms": int,
- "status": "ok"|"warning"|"error", "detail": str?},
- ...
- ],
- "time_to_trace_seconds": float,
- "output_csv_url": str,
- "lot_code": str,
- "fda_24h_mandate_seconds": 86400
+ "data_available": false,
+ "steps": [],
+ "time_to_trace_seconds": null,
+ "output_csv_url": null,
+ "lot_code": null,
+ "fda_24h_mandate_seconds": 86400,
+ "message": str,
+ "timestamp": str
}
"""
@@ -61,65 +45,12 @@
router = APIRouter(prefix="/api/v1/proof", tags=["Public Proof"])
-
-# ---------------------------------------------------------------------------
-# Frozen synthetic drill -- deterministic by construction
-# ---------------------------------------------------------------------------
-# This corresponds to the public CSV at
-# ``frontend/public/samples/sample_recall_drill.csv`` -- a known-contaminated
-# romaine lot harvested at Sunny Valley Farms, traced forward through the
-# 7 CTEs (harvesting, cooling, initial packing, shipping, receiving,
-# transformation, shipping) into Metro Foods, Valley Wholesale, and
-# SuperMart #42.
-FROZEN_LOT_CODE = "SV-20240115-001"
-OUTPUT_CSV_URL = "/samples/sample_recall_drill.csv"
+# 21 CFR 1.1455(c)(3): records must be provided to FDA within 24 hours.
FDA_24H_MANDATE_SECONDS = 86400
-# Steps mirror the synthetic frontend script so a UI consuming this
-# endpoint produces the same per-step animation. Durations sum to
-# 7.0 seconds — well inside the 24h FDA mandate.
-_FROZEN_STEPS: tuple[dict, ...] = (
- {
- "name": "Identifying source lot",
- "duration_ms": 1200,
- "status": "ok",
- "detail": (
- "Loaded contaminated lot SV-20240115-001 (Romaine Lettuce, "
- "500 cases) from Sunny Valley Farms (GLN 1234567890128)."
- ),
- },
- {
- "name": "Tracing forward through 7 CTEs",
- "duration_ms": 2400,
- "status": "ok",
- "detail": (
- "Walked CTE-1 HARVESTING -> CTE-2 COOLING -> CTE-3 "
- "INITIAL_PACKING -> CTE-4 SHIPPING -> CTE-5 RECEIVING -> "
- "CTE-6 TRANSFORMATION -> CTE-7 SHIPPING. Resolved 2 child "
- "lots (FC-20240116-A, FC-20240116-B) at the transformation "
- "node."
- ),
- },
- {
- "name": "Walking downstream graph edges",
- "duration_ms": 1800,
- "status": "ok",
- "detail": (
- "Visited 11 events across 5 facilities. Reached 3 "
- "downstream receivers: Metro Foods, Valley Wholesale, "
- "SuperMart #42."
- ),
- },
- {
- "name": "Generating FDA-oriented recall CSV",
- "duration_ms": 1600,
- "status": "ok",
- "detail": (
- "Wrote 21 CFR §§1.1325–1.1350 Key Data Elements for each downstream "
- "lot. Hash-anchored each record (SHA-256 chain integrity "
- "preserved)."
- ),
- },
+NO_DATA_MESSAGE = (
+ "No recall drill data is available on this public endpoint. "
+ "Run a recall trace against your own records after signing in."
)
@@ -134,11 +65,13 @@ class DrillStep(BaseModel):
class RecallDrillResponse(BaseModel):
- steps: list[DrillStep]
- time_to_trace_seconds: float
- output_csv_url: str
- lot_code: str
+ data_available: bool = False
+ steps: list[DrillStep] = Field(default_factory=list)
+ time_to_trace_seconds: Optional[float] = None
+ output_csv_url: Optional[str] = None
+ lot_code: Optional[str] = None
fda_24h_mandate_seconds: int = FDA_24H_MANDATE_SECONDS
+ message: str = NO_DATA_MESSAGE
timestamp: str
@@ -157,22 +90,8 @@ def _hash_ip(client_host: Optional[str]) -> str:
def _build_response() -> RecallDrillResponse:
- """Build the deterministic drill response.
-
- Pure function of module constants — no I/O, no tenant lookups.
- Two calls return identical ``steps`` / ``time_to_trace_seconds``
- (only ``timestamp`` differs).
- """
- steps = [DrillStep(**s) for s in _FROZEN_STEPS]
- total_ms = sum(s.duration_ms for s in steps)
- return RecallDrillResponse(
- steps=steps,
- time_to_trace_seconds=round(total_ms / 1000.0, 3),
- output_csv_url=OUTPUT_CSV_URL,
- lot_code=FROZEN_LOT_CODE,
- fda_24h_mandate_seconds=FDA_24H_MANDATE_SECONDS,
- timestamp=_now_iso(),
- )
+ """Build the honest empty response. No I/O, no tenant lookups."""
+ return RecallDrillResponse(timestamp=_now_iso())
# ---------------------------------------------------------------------------
@@ -181,30 +100,20 @@ def _build_response() -> RecallDrillResponse:
@router.post(
"/recall-drill",
response_model=RecallDrillResponse,
- summary="Run a deterministic recall drill against a frozen synthetic dataset",
+ summary="Public recall drill: returns an honest empty result (no tenant data is read)",
)
@limiter.limit("5/minute")
async def run_recall_drill(request: Request) -> RecallDrillResponse:
- """Run a public recall drill against the frozen synthetic dataset.
-
- Takes no body. Returns a deterministic step list plus the
- wall-clock ``time_to_trace_seconds`` for the synthetic drill, the
- FDA 24-hour response target for client-side comparison, and the
- URL of a pre-generated FDA-oriented recall CSV.
+ """Return an honest empty recall-drill result.
- This endpoint MUST NOT read any tenant data: it operates entirely
- against module-level constants derived from
- ``frontend/public/samples/sample_recall_drill.csv``. No database,
- queue, or tenant store is touched.
+ Takes no body and reads no tenant data, so there is nothing real to
+ trace: ``steps`` is empty, ``lot_code``, ``time_to_trace_seconds``
+ and ``output_csv_url`` are null, and ``data_available`` is false.
"""
ip_hash = _hash_ip(request.client.host if request.client else None)
response = _build_response()
logger.info(
"proof_recall_drill_call",
- extra={
- "ip_hash": ip_hash,
- "lot_code": response.lot_code,
- "time_to_trace_seconds": response.time_to_trace_seconds,
- },
+ extra={"ip_hash": ip_hash, "data_available": response.data_available},
)
return response
diff --git a/services/ingestion/tests/test_proof_recall_router.py b/services/ingestion/tests/test_proof_recall_router.py
index ec60d415f..f10076ee4 100644
--- a/services/ingestion/tests/test_proof_recall_router.py
+++ b/services/ingestion/tests/test_proof_recall_router.py
@@ -12,11 +12,14 @@
- Failure modes: the 6th request inside a one-minute window returns
429 (rate-limit triggers), and the response is deterministic across
successive successful calls.
+- Real data only: the endpoint returns an honest empty result (no
+ steps, no lot code, no trace time, no CSV) instead of a synthetic drill.
"""
from __future__ import annotations
import os
+import uuid
import sys
from itertools import count
@@ -28,8 +31,6 @@
from slowapi.util import get_remote_address
FDA_24H_MANDATE_SECONDS = proof_recall_module.FDA_24H_MANDATE_SECONDS
-FROZEN_LOT_CODE = proof_recall_module.FROZEN_LOT_CODE
-OUTPUT_CSV_URL = proof_recall_module.OUTPUT_CSV_URL
proof_recall_router = proof_recall_module.router
limiter = proof_recall_module.limiter
_client_counter = count(1)
@@ -107,8 +108,8 @@ def test_endpoint_accepts_no_tenant_header_and_works(self, client: TestClient):
resp = client.post("/api/v1/proof/recall-drill")
assert resp.status_code == 200, resp.text
data = resp.json()
- assert data["lot_code"] == FROZEN_LOT_CODE
- assert data["output_csv_url"] == OUTPUT_CSV_URL
+ assert data["lot_code"] is None
+ assert data["output_csv_url"] is None
def test_module_does_not_import_tenant_scoped_clients(self):
"""Defense-in-depth: the recall-drill module must not import
@@ -139,7 +140,7 @@ def test_tenant_id_header_is_ignored(self, client: TestClient):
resp_a = client.post("/api/v1/proof/recall-drill")
resp_b = client.post(
"/api/v1/proof/recall-drill",
- headers={"X-Tenant-ID": "00000000-0000-0000-0000-000000000000"},
+ headers={"X-Tenant-ID": str(uuid.uuid4())},
)
assert resp_a.status_code == 200
assert resp_b.status_code == 200
@@ -167,33 +168,20 @@ def test_response_shape_matches_spec(self, client: TestClient):
"output_csv_url",
"lot_code",
"fda_24h_mandate_seconds",
+ "data_available",
+ "message",
}
# Constants.
assert data["fda_24h_mandate_seconds"] == FDA_24H_MANDATE_SECONDS
- assert data["lot_code"] == FROZEN_LOT_CODE
- assert data["output_csv_url"] == OUTPUT_CSV_URL
-
- # time_to_trace_seconds must be a positive number well under
- # the FDA 24h mandate (otherwise the demo is misleading).
- assert isinstance(data["time_to_trace_seconds"], (int, float))
- assert 0 < data["time_to_trace_seconds"] < FDA_24H_MANDATE_SECONDS
-
- # Steps shape.
- steps = data["steps"]
- assert isinstance(steps, list) and len(steps) >= 1
- valid_status = {"ok", "warning", "error"}
- total_ms = 0
- for s in steps:
- assert set(s.keys()) >= {"name", "duration_ms", "status"}
- assert isinstance(s["name"], str) and s["name"]
- assert isinstance(s["duration_ms"], int) and s["duration_ms"] >= 0
- assert s["status"] in valid_status
- total_ms += s["duration_ms"]
-
- # time_to_trace_seconds must equal the sum of step durations
- # (deterministic by construction).
- assert abs(data["time_to_trace_seconds"] - total_ms / 1000.0) < 1e-6
+ assert data["lot_code"] is None
+ assert data["output_csv_url"] is None
+
+ # Honest empty result: no invented drill, steps, timings or CSV.
+ assert data["data_available"] is False
+ assert data["steps"] == []
+ assert data["time_to_trace_seconds"] is None
+ assert isinstance(data["message"], str) and data["message"]
def test_get_method_not_allowed(self, client: TestClient):
"""GET on a POST-only route returns 405."""
@@ -245,3 +233,17 @@ def test_response_is_deterministic_across_calls(self, client: TestClient):
a.pop("timestamp", None)
b.pop("timestamp", None)
assert a == b
+
+
+# ---------------------------------------------------------------------------
+# Real data only — no synthetic drill content is served
+# ---------------------------------------------------------------------------
+class TestNoSyntheticData:
+ def test_module_has_no_frozen_dataset(self):
+ for name in ("FROZEN_LOT_CODE", "OUTPUT_CSV_URL", "_FROZEN_STEPS"):
+ assert not hasattr(proof_recall_module, name), name
+
+ def test_response_references_no_sample_assets(self, client: TestClient):
+ resp = client.post("/api/v1/proof/recall-drill")
+ assert resp.status_code == 200, resp.text
+ assert "/samples/" not in resp.text
From db3f401109f16f0161c2b6abb870abd5f750fd8d Mon Sep 17 00:00:00 2001
From: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
Date: Fri, 23 Oct 2026 14:35:47 -0700
Subject: [PATCH 2/5] style(ingestion): sort imports in proof recall router
test
Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
---
services/ingestion/tests/test_proof_recall_router.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/services/ingestion/tests/test_proof_recall_router.py b/services/ingestion/tests/test_proof_recall_router.py
index f10076ee4..7300b1f8c 100644
--- a/services/ingestion/tests/test_proof_recall_router.py
+++ b/services/ingestion/tests/test_proof_recall_router.py
@@ -19,8 +19,8 @@
from __future__ import annotations
import os
-import uuid
import sys
+import uuid
from itertools import count
import app.proof_recall_router as proof_recall_module
From c455b18b6ef3e816bc256390d19b04864704be0c Mon Sep 17 00:00:00 2001
From: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
Date: Fri, 23 Oct 2026 14:37:57 -0700
Subject: [PATCH 3/5] fix(proof): render honest empty state on recall-drill
page, drop fake drill data
Remove DEFAULT_DRILL_STEPS/DEFAULT_DRILL_RESPONSE fallbacks, fake lot,
supplier names, trace graph and sample CSV link. Show the API message when
data_available is false; never derive a trace time. Update test to be
data-free and correct stale synthetic-drill mentions in docs.
Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
---
PRODUCTION_SURFACE.md | 4 +-
docs/PRODUCT_DESCRIPTION.md | 4 +-
docs/security/TRUST_CLAIMS_MATRIX.md | 2 +-
.../pages/proof-recall-drill.test.tsx | 91 +++--
frontend/src/app/case-studies/page.tsx | 4 +-
frontend/src/app/proof/recall-drill/page.tsx | 341 ++++--------------
6 files changed, 144 insertions(+), 302 deletions(-)
diff --git a/PRODUCTION_SURFACE.md b/PRODUCTION_SURFACE.md
index 4577b0128..30b37437b 100644
--- a/PRODUCTION_SURFACE.md
+++ b/PRODUCTION_SURFACE.md
@@ -23,7 +23,7 @@ These surfaces are part of the core production spine when their corresponding ro
token-scoped supplier correction session (`remediation`; compliance-critical,
see "Boot-critical routers" below)
- FDA export and export monitoring
-- Public proof endpoints: export-bundle verification and the recall drill
+- Public proof endpoints: export-bundle verification and the recall-drill endpoint (currently returns no drill data)
(`proof_verify`, `proof_recall`; tenantless and unauthenticated, see below)
- Tenant, user, API-key, supplier, and audit-log administration
- Billing routes required for customer subscription management
@@ -85,7 +85,7 @@ so `ENABLE_EXPERIMENTAL_ROUTERS` does not gate them:
Public export-bundle hash-chain/Merkle verifier. No auth, no tenant
context. Rate limit 10 req/min/IP.
- `proof_recall` — `POST /api/v1/proof/recall-drill` (`server/main.py:654`).
- Public deterministic recall drill over a frozen synthetic dataset. No auth,
+ Public recall-drill endpoint. It reads no customer data and currently returns an honest empty result (`data_available: false`, no steps, no lot, no trace time, no CSV) with an explanatory message. No auth,
no tenant context. Rate limit 5 req/min/IP.
Both carry an in-module justification docstring arguing they belong to the
diff --git a/docs/PRODUCT_DESCRIPTION.md b/docs/PRODUCT_DESCRIPTION.md
index 7666cb8e4..c3ceb416c 100644
--- a/docs/PRODUCT_DESCRIPTION.md
+++ b/docs/PRODUCT_DESCRIPTION.md
@@ -14,7 +14,7 @@ Accepted events are stored with tenant boundaries in PostgreSQL. The platform ev
Optional Jev assistance suggests mappings for unfamiliar CSV columns and a file's CTE when none is declared. Upload screens expose suggestions for operator review. Automatic application defaults off, requests use headers without row samples by default, and model suggestions do not replace deterministic validation. Preview and evaluation tools let operators inspect suggestions before enabling automatic application.
-Public synthetic artifacts, a browser verifier, a downloadable Python bundle verifier, and a reproducible recall drill let reviewers inspect the evidence format. Successful hash or Merkle verification establishes artifact integrity; it does not establish source-data accuracy or regulatory approval.
+Public synthetic artifacts, a browser verifier, a downloadable Python bundle verifier let reviewers inspect the evidence format. Successful hash or Merkle verification establishes artifact integrity; it does not establish source-data accuracy or regulatory approval. The public recall-drill endpoint currently returns no drill data; it reports that honestly rather than showing a sample drill.
## Capability and availability matrix
@@ -29,7 +29,7 @@ Public synthetic artifacts, a browser verifier, a downloadable Python bundle ver
| Tenant-scoped records and tamper-evident chains | [Tenant authority](../services/shared/tenant_context.py), [canonical persistence](../services/shared/canonical_persistence/writer.py), [canonical chain verification](../services/shared/canonical_persistence/verification.py), [admin audit integrity](../services/admin/app/audit_integrity.py). See [persistence cutover](persistence/dual-write-cutover.md). |
| FDA-oriented exports and request-response packages | Release-enabled: [FDA export](../services/ingestion/app/fda_export/router.py), [request workflow](../services/ingestion/app/request_workflow_router.py), [auditor API](../services/ingestion/app/auditor_router.py). |
| Readiness assessments and recall reporting | Release-enabled: [readiness](../services/ingestion/app/readiness_router.py), [recall reports](../services/ingestion/app/recall_report.py). |
-| Public proof and synthetic recall drill | [Proof page](https://regengine.co/proof), [browser verifier](https://regengine.co/verify), [recall drill](https://regengine.co/proof/recall-drill), [verifier source](../frontend/public/sdk/verify_chain.py). Synthetic demonstration data. |
+| Public proof and recall-drill endpoint | [Proof page](https://regengine.co/proof), [browser verifier](https://regengine.co/verify), [recall drill](https://regengine.co/proof/recall-drill), [verifier source](../frontend/public/sdk/verify_chain.py). Synthetic verification artifacts; the recall-drill endpoint currently returns no drill data. |
| Supplier manifest remediation and correction links | Implemented in [remediation](../services/ingestion/app/remediation/) and the [buyer dashboard](../frontend/src/app/remediation/page.tsx); excluded from the production router allowlist. Notification delivery also requires recipient/provider configuration. |
| EPCIS JSON-LD export and broader recall simulations | Implemented in [EPCIS export](../services/ingestion/app/epcis_export.py) and [simulations](../services/ingestion/app/recall_simulations.py); these routers are experimental and excluded from the production allowlist. Public sample EPCIS downloads are a separate proof surface. |
| Inbound email CSV ingestion | [Postmark integration](../services/ingestion/app/email_ingest/); excluded from the production allowlist, with no self-service provisioning UI. See [rollout notes](engineering/EMAIL_INGESTION.md). |
diff --git a/docs/security/TRUST_CLAIMS_MATRIX.md b/docs/security/TRUST_CLAIMS_MATRIX.md
index 685bbebf4..41de2ae50 100644
--- a/docs/security/TRUST_CLAIMS_MATRIX.md
+++ b/docs/security/TRUST_CLAIMS_MATRIX.md
@@ -28,7 +28,7 @@ Do not use these phrases in public or procurement copy without owner approval an
| COM-001 | Design-partner pricing and rate commitments are contract-controlled. | Review each use | Public / Contract | Pricing, order forms, procurement | Pricing page and executed terms | Founder / Legal | `frontend/src/components/pricing/PricingPageClient.tsx`, `frontend/src/app/pricing/page.tsx` | 2026-06-24 | "Rate controlled by your executed design-partner terms." | "Locked forever", "no increases ever" unless order form says so | Before pricing-page release |
| COM-002 | Competitor comparisons are qualitative unless individually sourced. | Review each use | Public | Pricing, sales decks | Pricing page | Product / Legal | `frontend/src/app/pricing/page.tsx`, `frontend/src/components/pricing/PricingPageClient.tsx` | 2026-06-24 | "Validate competitor capabilities directly before procurement decisions." | Checkmarks or unsupported feature claims without citations | Before pricing-page release |
| INT-001 | Integration status taxonomy is conservative and status labels are source-controlled. | Approved | Public | Trust, integrations, dashboard | Customer readiness registry | Product / Engineering | `frontend/src/lib/customer-readiness.ts`, `frontend/src/app/trust/page.tsx`, `frontend/src/app/integrations/page.tsx` | 2026-06-24 | "Implemented", "Supported", "Pilot", "Custom Scoped", "Contract-only." | "Native Bidirectional" unless truly supported | Monthly and before registry additions |
-| MET-001 | Synthetic benchmark metrics are not customer ROI, SLA, or production-performance commitments. | Review each use | Public | Case studies, proof pages, sales decks | Case-study methodology and claims matrix | Product / Legal | `frontend/src/app/case-studies/page.tsx`, `frontend/src/app/proof/recall-drill/page.tsx` | 2026-06-24 | "Synthetic validation workflow modeled on leafy-greens supply chains; not customer ROI, SLA, or production-performance commitments." | "No projections", "customer-proven savings", "guaranteed recall time" without executed evidence | Before publishing metrics |
+| MET-001 | Synthetic benchmark metrics are not customer ROI, SLA, or production-performance commitments. | Review each use | Public | Case studies, proof pages, sales decks | Case-study methodology and claims matrix | Product / Legal | `frontend/src/app/case-studies/page.tsx` | 2026-06-24 | "Synthetic validation workflow modeled on leafy-greens supply chains; not customer ROI, SLA, or production-performance commitments." | "No projections", "customer-proven savings", "guaranteed recall time" without executed evidence | Before publishing metrics |
| AVAIL-001 | Guided onboarding and self-serve language must reflect design-partner availability. | Review each use | Public / Contract | Pricing, case studies, onboarding pages | Trust Center maturity banner and pricing page | Product / Legal | `frontend/src/app/pricing/page.tsx`, `frontend/src/components/pricing/PricingPageClient.tsx`, `frontend/src/app/case-studies/page.tsx` | 2026-06-24 | "Guided onboarding is available for approved design-partner workspaces; timeline, support, and commercial terms are confirmed during scoping." | "Self-serve in minutes", "no contract negotiation", "public checkout available" unless billing is aligned | Before pricing-page release |
| EXT-001 | External market, retailer, and recall-cost claims require citations or conservative caveats. | Review each use | Public | Case studies, blogs, sales decks | Source citation or buyer validation | Product / Legal | `frontend/src/app/case-studies/page.tsx`, `frontend/src/app/blog/` | 2026-06-24 | "Retailer requirements vary and should be confirmed directly with each buyer." | "Walmart/Kroger require..." without current citation and qualification | Monthly and before sales use |
| BILL-001 | Public price, CTA, checkout, and order-form consistency must be verified before self-serve claims. | Review each use | Public / Contract | Pricing, checkout, terms | Billing config and executed order form | Product / Finance / Legal | `frontend/src/app/pricing/page.tsx`, `frontend/src/components/billing/PricingCheckoutButton.tsx`, `services/ingestion/app/stripe_billing/plans.py` | 2026-06-24 | "Pricing shown is indicative; executed agreement controls final plan, billing period, and checkout availability." | "Start plan" / direct checkout claims when plan IDs or billing terms are not aligned | Before billing/pricing releases |
diff --git a/frontend/src/__tests__/pages/proof-recall-drill.test.tsx b/frontend/src/__tests__/pages/proof-recall-drill.test.tsx
index 2b0f65497..f2a63bd5b 100644
--- a/frontend/src/__tests__/pages/proof-recall-drill.test.tsx
+++ b/frontend/src/__tests__/pages/proof-recall-drill.test.tsx
@@ -1,49 +1,76 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+import { afterEach, describe, expect, it, vi } from 'vitest';
import RecallDrillPage from '@/app/proof/recall-drill/page';
-describe('public proof recall drill page', () => {
- beforeEach(() => {
- vi.stubGlobal(
- 'fetch',
- vi.fn().mockResolvedValue({
- ok: true,
- json: async () => ({
- steps: [
- {
- name: 'Identifying from API',
- duration_ms: 100,
- status: 'ok',
- detail: 'Endpoint loaded the frozen public dataset.',
- },
- ],
- time_to_trace_seconds: 0.1,
- output_csv_url: '/samples/sample_recall_drill.csv',
- lot_code: 'SV-20240115-001',
- fda_24h_mandate_seconds: 86400,
- timestamp: '2026-05-19T00:00:00Z',
- }),
- }),
- );
- });
+const uid = () => crypto.randomUUID();
+
+function stubFetch(body: unknown, ok = true) {
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok, status: ok ? 200 : 503, json: async () => body }));
+}
+describe('public proof recall drill page', () => {
afterEach(() => {
vi.unstubAllGlobals();
});
- it('starts the public backend drill endpoint and renders returned steps', async () => {
+ it('renders the API message as an empty state when data_available is false', async () => {
+ const message = uid();
+ stubFetch({
+ data_available: false,
+ steps: [],
+ time_to_trace_seconds: null,
+ output_csv_url: null,
+ lot_code: null,
+ fda_24h_mandate_seconds: 86400,
+ message,
+ timestamp: new Date().toISOString(),
+ });
const user = userEvent.setup();
render(
- These metrics are reproducible. Try the same recall drill at{' '} - /proof/recall-drill. + The public recall-drill endpoint at{' '} + /proof/recall-drill currently returns no drill data and does not reproduce these metrics.
We ran a synthetic validation workflow modeled on leafy-greens supply chains (from Salinas Valley
diff --git a/frontend/src/app/proof/recall-drill/page.tsx b/frontend/src/app/proof/recall-drill/page.tsx
index 4e21e1a29..6a26fa543 100644
--- a/frontend/src/app/proof/recall-drill/page.tsx
+++ b/frontend/src/app/proof/recall-drill/page.tsx
@@ -1,25 +1,14 @@
'use client';
import { useState, useRef, useEffect } from 'react';
-import {
- AlertTriangle,
- Play,
- Download,
- CheckCircle2,
- Clock,
- ArrowRight,
- RotateCcw,
- Lock,
-} from 'lucide-react';
+import { Play, Download, CheckCircle2, Clock, RotateCcw, Lock, Inbox } from 'lucide-react';
import { Container } from '@/components/layout/container';
import { PageHeader } from '@/components/layout/page-header';
-import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Callout } from '@/components/ui/callout';
import { Card, CardHeader } from '@/components/ui/card';
import { StatTile } from '@/components/ui/stat-tile';
-const RECALL_CSV_URL = '/samples/sample_recall_drill.csv';
const PROOF_RECALL_ENDPOINT = '/api/ingestion/api/v1/proof/recall-drill';
type DrillStep = {
@@ -36,90 +25,18 @@ type RecallDrillApiStep = {
detail?: string | null;
};
-type RecallDrillResponse = {
+export type RecallDrillResponse = {
+ data_available: boolean;
steps: RecallDrillApiStep[];
- time_to_trace_seconds: number;
- output_csv_url: string;
- lot_code: string;
+ time_to_trace_seconds: number | null;
+ output_csv_url: string | null;
+ lot_code: string | null;
fda_24h_mandate_seconds: number;
+ message: string;
timestamp: string;
};
-const DEFAULT_DRILL_STEPS: DrillStep[] = [
- {
- label: 'Identifying source lot',
- detailLines: [
- 'Loaded contaminated lot: SV-20240115-001 (Romaine Lettuce, 500 cases)',
- 'Origin: Sunny Valley Farms (GLN 1234567890128)',
- ],
- durationMs: 1200,
- status: 'ok',
- },
- {
- label: 'Tracing forward through 7 CTEs',
- detailLines: [
- 'CTE-1 HARVESTING → CTE-2 COOLING → CTE-3 INITIAL_PACKING',
- 'CTE-4 SHIPPING → CTE-5 RECEIVING → CTE-6 TRANSFORMATION → CTE-7 SHIPPING',
- 'Resolved 2 child lots (FC-20240116-A, FC-20240116-B) at transformation node',
- ],
- durationMs: 2400,
- status: 'ok',
- },
- {
- label: 'Walking downstream graph edges',
- detailLines: [
- 'Visited 11 events across 5 facilities',
- 'Reached 3 receivers: Metro Foods, Valley Wholesale, SuperMart #42',
- ],
- durationMs: 1800,
- status: 'ok',
- },
- {
- label: 'Generating FDA-oriented sortable recall CSV from sample data',
- detailLines: [
- 'Wrote 21 CFR §§1.1325 to 1.1350 Key Data Elements for each downstream lot',
- 'Hash-anchored each record (SHA-256 chain integrity preserved)',
- ],
- durationMs: 1600,
- status: 'ok',
- },
-];
-
-/* Final trace nodes used in the result panel diagram. */
-type TraceNode = {
- id: string;
- facility: string;
- cte: string;
- lot: string;
- qty: string;
-};
-
-const TRACE_NODES: TraceNode[] = [
- { id: 'h', facility: 'Sunny Valley Farms', cte: 'HARVESTING', lot: 'SV-20240115-001', qty: '500 cs' },
- { id: 's1', facility: 'Sunny Valley Farms', cte: 'SHIPPING', lot: 'SV-20240115-001', qty: '500 cs' },
- { id: 'r1', facility: 'Fresh Cut Co', cte: 'RECEIVING', lot: 'SV-20240115-001', qty: '500 cs' },
- { id: 't', facility: 'Fresh Cut Co', cte: 'TRANSFORMATION', lot: 'FC-20240116-A / -B', qty: '250 + 250 cs' },
- { id: 's2', facility: 'Fresh Cut Co', cte: 'SHIPPING', lot: 'FC-20240116-A', qty: '100 cs' },
- { id: 'r2', facility: 'Metro Foods', cte: 'RECEIVING', lot: 'FC-20240116-A', qty: '100 cs' },
- { id: 's3', facility: 'Metro Foods', cte: 'SHIPPING', lot: 'FC-20240116-A', qty: '50 cs' },
- { id: 'r3', facility: 'SuperMart #42', cte: 'RECEIVING', lot: 'FC-20240116-A', qty: '50 cs' },
-];
-
-const DEFAULT_DRILL_RESPONSE: RecallDrillResponse = {
- steps: DEFAULT_DRILL_STEPS.map((step) => ({
- name: step.label,
- duration_ms: step.durationMs,
- status: step.status,
- detail: step.detailLines.join(' '),
- })),
- time_to_trace_seconds: DEFAULT_DRILL_STEPS.reduce((acc, s) => acc + s.durationMs, 0) / 1000,
- output_csv_url: RECALL_CSV_URL,
- lot_code: 'SV-20240115-001',
- fda_24h_mandate_seconds: 86400,
- timestamp: '',
-};
-
-type DrillState = 'idle' | 'loading' | 'running' | 'done';
+type DrillState = 'idle' | 'loading' | 'running' | 'done' | 'empty';
function normalizeStep(step: RecallDrillApiStep): DrillStep {
return {
@@ -130,15 +47,8 @@ function normalizeStep(step: RecallDrillApiStep): DrillStep {
};
}
-function normalizeDrill(data: RecallDrillResponse): {
- response: RecallDrillResponse;
- steps: DrillStep[];
-} {
- const steps = data.steps.map(normalizeStep).filter((step) => step.durationMs >= 0);
- return {
- response: data,
- steps: steps.length > 0 ? steps : DEFAULT_DRILL_STEPS,
- };
+function normalizeSteps(data: RecallDrillResponse): DrillStep[] {
+ return (data.steps ?? []).map(normalizeStep).filter((step) => step.durationMs >= 0);
}
function formatSeconds(value: number): string {
@@ -149,8 +59,8 @@ export default function RecallDrillPage() {
const [state, setState] = useState
- Click Start drill and RegEngine
- traces the contaminated lot through the applicable CTEs and produces an FDA-oriented sortable
- recall CSV in under 60 seconds. Synthetic data, illustrative only.
+ This public endpoint reads no customer data. It shows a drill only when the API
+ returns one; otherwise it tells you no drill data is available.