diff --git a/README.md b/README.md index 77e422977..e9f9fce80 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ RegEngine is an API-first traceability evidence platform for food supply chains. Inflow Workbench prepares data with preflight checks, readiness scores, a fix queue, and a commit gate. Optional Jev assistance suggests CSV column mappings and missing event types; deterministic validation and authenticated tenant boundaries remain authoritative. -[Website](https://regengine.co) · [Public proof](https://regengine.co/proof) · [Verify a bundle](https://regengine.co/verify) · [Product description](docs/PRODUCT_DESCRIPTION.md) · [Python SDK](regengine/README.md) +[Website](https://regengine.co) · [Verify a bundle](https://regengine.co/verify) · [Product description](docs/PRODUCT_DESCRIPTION.md) · [Python SDK](regengine/README.md) This is a pre-production application with deployed public proof surfaces. The capability inventory below describes checked-in implementation and release policy, not proof of customer adoption or live configuration. @@ -381,7 +381,7 @@ RegEngine is proprietary, source-visible software — all rights reserved by Reg > **Before changing this repository's visibility**, work through [docs/security/PRE_PUBLIC_CHECKLIST.md](docs/security/PRE_PUBLIC_CHECKLIST.md). Confidential business material is currently tracked in-tree, and `git rm` removes it from `HEAD` only — every prior revision stays readable. -Permissive exceptions — the public verifier tools are **Apache 2.0** (declared in [LICENSE](LICENSE)) so anyone can use, copy, and embed them: the `/proof` Merkle bundle verifier ([frontend/public/sdk/verify_chain.py](frontend/public/sdk/verify_chain.py)) and the `regengine-verify` per-record CLI ([regengine/verify_records.py](regengine/verify_records.py)). [Inflow Lab](https://github.com/regengine/inflow-lab), the integration simulator, is Apache 2.0 in its own repository for the same reason. +Permissive exceptions — the public verifier tools are **Apache 2.0** (declared in [LICENSE](LICENSE)) so anyone can use, copy, and embed them: the Merkle bundle verifier ([frontend/public/sdk/verify_chain.py](frontend/public/sdk/verify_chain.py)) and the `regengine-verify` per-record CLI ([regengine/verify_records.py](regengine/verify_records.py)). [Inflow Lab](https://github.com/regengine/inflow-lab), the integration simulator, is Apache 2.0 in its own repository for the same reason. ## Contributing diff --git a/docs/PRODUCT_DESCRIPTION.md b/docs/PRODUCT_DESCRIPTION.md index 6f4f38df4..a014a8583 100644 --- a/docs/PRODUCT_DESCRIPTION.md +++ b/docs/PRODUCT_DESCRIPTION.md @@ -29,7 +29,7 @@ A browser verifier and a downloadable Python bundle verifier let reviewers inspe | 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 recall-drill endpoint | [Proof page](https://regengine.co/proof), [browser verifier](https://regengine.co/verify), [verifier source](../frontend/public/sdk/verify_chain.py). Synthetic verification artifacts; the recall-drill endpoint currently returns no drill data. | +| Bundle verifier and recall-drill endpoint | [Browser verifier](https://regengine.co/verify), [verifier source](../frontend/public/sdk/verify_chain.py). 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). | @@ -48,7 +48,7 @@ A browser verifier and a downloadable Python bundle verifier let reviewers inspe ## Talking-track opener -RegEngine turns supplier and ERP records into traceability evidence for food businesses. Import records, identify missing or inconsistent CTEs and KDEs, resolve issues, and assemble FDA-oriented exports with verifiable audit evidence. Inflow Workbench prepares the data; RegEngine stores and verifies the evidence. Inspect the synthetic examples at [regengine.co/proof](https://regengine.co/proof) and check a bundle at [regengine.co/verify](https://regengine.co/verify). +RegEngine turns supplier and ERP records into traceability evidence for food businesses. Import records, identify missing or inconsistent CTEs and KDEs, resolve issues, and assemble FDA-oriented exports with verifiable audit evidence. Inflow Workbench prepares the data; RegEngine stores and verifies the evidence. Check a bundle at [regengine.co/verify](https://regengine.co/verify). ## Maintenance rule diff --git a/docs/security/PRE_PUBLIC_CHECKLIST.md b/docs/security/PRE_PUBLIC_CHECKLIST.md index 4343aae41..b85acb99d 100644 --- a/docs/security/PRE_PUBLIC_CHECKLIST.md +++ b/docs/security/PRE_PUBLIC_CHECKLIST.md @@ -72,6 +72,6 @@ it reactively at flip time.** - `REPO_PURPOSE.md` — the scope these files fall outside of. - `SECURITY.md` — reporting and disclosure posture. -- The public proof surface (`/proof`, `/verify`) already provides +- The public verifier (`/verify`) already provides transparency and audit **without** requiring source visibility. It is the reason the README's public-repository claim sounded plausible. diff --git a/frontend/README.md b/frontend/README.md index f126501d7..789d7e4a7 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -15,7 +15,7 @@ See the [product description](../docs/PRODUCT_DESCRIPTION.md) for capability ava | Buyer manifest remediation and correction actions | [remediation](src/app/remediation/) — requires the remediation API, which is excluded from the production allowlist | | Recall reporting and audit views | [recall report](src/app/dashboard/recall-report/), [audit](src/app/audit/) | | Email-code/passkey login, invitations, and API keys | [login](src/app/login/), [invite acceptance](src/app/accept-invite/), [API keys](src/app/api-keys/) | -| Public synthetic proof, bundle verification, and recall drill | [proof](src/app/proof/), [verify](src/app/verify/) | +| Bundle verification and recall drill | [verify](src/app/verify/), [recall drills](src/app/dashboard/recall-drills/) | | Product documentation and developer portal | [docs](src/app/docs/), [developer portal](src/app/developer/portal/) | Jev assistance is optional. Suggestions are visible for review; automatic application defaults off. See the [Jev guide](../docs/engineering/JEV_HEADER_MAPPING.md) for configuration, evidence, and data-sharing defaults. diff --git a/frontend/src/__tests__/pages/empty-states-3554.test.ts b/frontend/src/__tests__/pages/empty-states-3554.test.ts new file mode 100644 index 000000000..957d514cc --- /dev/null +++ b/frontend/src/__tests__/pages/empty-states-3554.test.ts @@ -0,0 +1,66 @@ +import { existsSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { CAPABILITY_REGISTRY, STATUS_LABELS } from '@/lib/customer-readiness'; + +const read = (p: string) => readFileSync(resolve(process.cwd(), p), 'utf8'); + +describe('#3554 empty states and hidden sample artifacts', () => { + it('export-jobs and integrations show No data yet with CSV upload and JSON events API next steps', () => { + for (const page of ['src/app/dashboard/export-jobs/page.tsx', 'src/app/integrations/page.tsx']) { + const src = read(page); + expect(src).toContain('title={NO_DATA_YET}'); + expect(src).toMatch(/href="\/ingest"/); + expect(src).toMatch(/href="\/docs\/api"/); + } + }); + + it('support pages render Not set for empty channel fields', () => { + for (const page of ['src/app/settings/support/page.tsx', 'src/app/trust/support/page.tsx']) { + const src = read(page); + expect(src).toContain('channel.responseWindow || NOT_SET'); + expect(src).toContain('channel.notes || NOT_SET'); + } + }); + + it('labels the EPCIS capability as Preview', () => { + const epcis = CAPABILITY_REGISTRY.find((item) => item.id === 'epcis'); + expect(epcis?.name).toBe('GS1 EPCIS 2.0 (Preview)'); + expect(epcis && STATUS_LABELS[epcis.status]).toBe('Preview'); + }); + + it('no longer publishes the made-up proof samples', () => { + expect(existsSync(resolve(process.cwd(), 'public/proof/fsma-204-sample.csv'))).toBe(false); + expect(existsSync(resolve(process.cwd(), 'public/proof/epcis-sample.jsonld'))).toBe(false); + }); + + it('hides /proof/transformation and drops every link to it', () => { + expect(read('src/app/proof/transformation/page.tsx')).toContain('notFound()'); + expect(existsSync(resolve(process.cwd(), 'src/app/proof/transformation/opengraph-image.tsx'))).toBe(false); + expect(read('src/app/sitemap.ts')).not.toContain('/proof/transformation'); + expect(read('src/app/walkthrough/page.tsx')).not.toContain('/proof/transformation'); + }); + + it('removes the public proof zips', () => { + for (const f of ['public/proof/audit-bundle.zip', 'public/proof/audit-bundle-tampered.zip']) { + expect(existsSync(resolve(process.cwd(), f))).toBe(false); + } + }); + + it('security page shows No data yet instead of illustrative verifier output', () => { + const src = read('src/app/security/page.tsx'); + expect(src).not.toContain('Sample · illustrative'); + expect(src).toContain('title={NO_DATA_YET}'); + }); + + it('CTE pages ask for the user\'s own file, not a sample', () => { + const src = read('src/app/fsma-204/[cte]/page.tsx'); + expect(src).not.toContain('Upload a sample'); + // After merging main (#3557 retired /sandbox), the closing CTA copy comes from + // sandboxCta().blurb, which asks for the user's own records and links /ingest. + expect(src).toContain('{cta.blurb}'); + expect(src).not.toContain('sandbox and get an instant'); + expect(src).not.toContain('data to the sandbox'); + expect(src).toMatch(/data to\{" "\}\s*]*>RegEngine<\/Link>/); + }); +}); diff --git a/frontend/src/__tests__/pages/pre-revenue-copy.test.ts b/frontend/src/__tests__/pages/pre-revenue-copy.test.ts index 52a2ae3a7..95eb9275c 100644 --- a/frontend/src/__tests__/pages/pre-revenue-copy.test.ts +++ b/frontend/src/__tests__/pages/pre-revenue-copy.test.ts @@ -13,7 +13,6 @@ const PUBLIC_FILES = [ 'src/app/compare/repositrak/page.tsx', 'src/app/supplier-compliance/page.tsx', 'src/app/tools/roi-calculator/components/ROICalculatorClient.tsx', - 'src/app/case-studies/page.tsx', 'src/app/developers/page.tsx', 'src/app/status/page.tsx', ]; @@ -59,10 +58,4 @@ describe('pre-revenue public copy', () => { expect(page).toContain('Pricing is temporarily unavailable'); expect(page).toContain('What does the Founding rate include?'); }); - - it('states pre-revenue status plainly on the case study disclosure', () => { - expect(read('src/app/case-studies/page.tsx')).toContain( - 'RegEngine is pre-revenue; self-serve access is in staged rollout.', - ); - }); }); diff --git a/frontend/src/__tests__/pages/proof-trust-copy.test.tsx b/frontend/src/__tests__/pages/proof-trust-copy.test.tsx index 13d875866..48b94fcd2 100644 --- a/frontend/src/__tests__/pages/proof-trust-copy.test.tsx +++ b/frontend/src/__tests__/pages/proof-trust-copy.test.tsx @@ -56,10 +56,10 @@ describe('public proof-bundle trust copy', () => { } }); - it('/proof states the checks do not authenticate the issuer', () => { + it('/proof shows the empty state instead of artifacts', () => { const text = renderedText(); - expect(text).toMatch(/do not authenticate the issuer/i); + expect(text).toMatch(/no public artifacts are published yet/i); for (const claim of UNBACKED_AUTHENTICITY_CLAIMS) { expect(text).not.toMatch(claim); diff --git a/frontend/src/__tests__/public-site-fixes.test.tsx b/frontend/src/__tests__/public-site-fixes.test.tsx index c3980f73b..b3311da2a 100644 --- a/frontend/src/__tests__/public-site-fixes.test.tsx +++ b/frontend/src/__tests__/public-site-fixes.test.tsx @@ -158,14 +158,7 @@ describe('public site launch-blocker fixes', () => { }); }); - it('labels synthetic validation honestly in the footer', () => { - expect(MARKETING_FOOTER_PRODUCT_LINKS).toContainEqual({ - label: 'Product Validation', - href: '/case-studies', - }); - expect(MARKETING_FOOTER_PRODUCT_LINKS).not.toContainEqual({ - label: 'Case Studies', - href: '/case-studies', - }); + it('keeps the hidden case-studies route out of the footer', () => { + expect(MARKETING_FOOTER_PRODUCT_LINKS.map((link) => link.href)).not.toContain('/case-studies'); }); }); diff --git a/frontend/src/app/admin/page.tsx b/frontend/src/app/admin/page.tsx index dd24a9583..25f397d28 100644 --- a/frontend/src/app/admin/page.tsx +++ b/frontend/src/app/admin/page.tsx @@ -311,7 +311,7 @@ export default function AdminPage() { title="API Key Created Successfully" className="mt-4" > -

Save this key now - it won't be shown again!

+

Save this key now. It won't be shown again!

{copiedKey} diff --git a/frontend/src/app/blog/fsma-204-cte-kde-guide/page.tsx b/frontend/src/app/blog/fsma-204-cte-kde-guide/page.tsx index 03b1561de..66128848c 100644 --- a/frontend/src/app/blog/fsma-204-cte-kde-guide/page.tsx +++ b/frontend/src/app/blog/fsma-204-cte-kde-guide/page.tsx @@ -424,7 +424,7 @@ export default function CTEKDEGuidePage() {

- Ready to implement FSMA 204 traceability in your business? See how RegEngine handles CTE/KDE capture, validation, and EPCIS-oriented export workflows. + Ready to implement FSMA 204 traceability in your business? See how RegEngine handles CTE/KDE capture, validation, and EPCIS-oriented export workflows (Preview).

- -
-
- -
- ); + notFound(); } diff --git a/frontend/src/app/controls/page.tsx b/frontend/src/app/controls/page.tsx index e4d6324a7..2db9ce1ff 100644 --- a/frontend/src/app/controls/page.tsx +++ b/frontend/src/app/controls/page.tsx @@ -282,7 +282,7 @@ export default function MyControlsPage() { > - + diff --git a/frontend/src/app/dashboard/export-jobs/page.tsx b/frontend/src/app/dashboard/export-jobs/page.tsx index 8ede51ad7..36d3e932b 100644 --- a/frontend/src/app/dashboard/export-jobs/page.tsx +++ b/frontend/src/app/dashboard/export-jobs/page.tsx @@ -11,6 +11,7 @@ import { Badge } from '@/components/ui/badge'; import { Callout } from '@/components/ui/callout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { EmptyState } from '@/components/ui/empty-state'; +import { NOT_SET, NO_DATA_YET } from '@/lib/empty-values'; import { Field } from '@/components/ui/field'; import { StatTile } from '@/components/ui/stat-tile'; import { Button } from '@/components/ui/button'; @@ -140,7 +141,7 @@ export default function ExportJobsPage() { )}
- + External archive required}>

Use scheduled exports to maintain long-term retention outside the app. @@ -215,20 +216,20 @@ export default function ExportJobsPage() { {job.status.replaceAll('_', ' ')}

- {job.format} · {job.cadence} · {job.destination} + {job.format || NOT_SET} · {job.cadence || NOT_SET} · {job.destination || NOT_SET}

@@ -249,8 +250,18 @@ export default function ExportJobsPage() {
+ Upload a CSV + + } + secondaryAction={ + + } />
{exportPrereqs.map((step) => ( diff --git a/frontend/src/app/dashboard/integrations/page.tsx b/frontend/src/app/dashboard/integrations/page.tsx index 3a3449785..82b5b538f 100644 --- a/frontend/src/app/dashboard/integrations/page.tsx +++ b/frontend/src/app/dashboard/integrations/page.tsx @@ -62,6 +62,7 @@ const STATUS_TONES: Record = { export_supported: 'info', file_import_supported: 'info', pilot: 'warning', + preview: 'warning', design_partner: 'warning', custom_scoped: 'neutral', preparation: 'neutral', diff --git a/frontend/src/app/dashboard/settings/page.tsx b/frontend/src/app/dashboard/settings/page.tsx index da897ff58..e9077e16f 100644 --- a/frontend/src/app/dashboard/settings/page.tsx +++ b/frontend/src/app/dashboard/settings/page.tsx @@ -49,7 +49,7 @@ const INTEGRATIONS = [ { id: 'netsuite', name: 'Oracle NetSuite', category: 'ERP', status: 'available', desc: 'Cloud ERP and financials' }, { id: 'walmart', name: 'Walmart GDSN', category: 'Retailer', status: 'coming_soon', desc: 'Global Data Synchronization' }, { id: 'kroger', name: 'Kroger 84.51\u00b0', category: 'Retailer', status: 'coming_soon', desc: 'Retailer data exchange' }, - { id: 'epcis', name: 'EPCIS 2.0 Gateway', category: 'Standards', status: 'epcis_preview', desc: 'GS1 event format bridge; ingest in Preview' }, + { id: 'epcis', name: 'EPCIS 2.0 Gateway (Preview)', category: 'Standards', status: 'epcis_preview', desc: 'GS1 event format bridge; ingest in Preview' }, { id: 'webhook', name: 'Custom Webhooks', category: 'Custom', status: 'preview', desc: 'Push events to your endpoints' }, ]; /** diff --git a/frontend/src/app/docs/api/ApiReferenceClient.tsx b/frontend/src/app/docs/api/ApiReferenceClient.tsx index 49d0a6870..89fc0b50e 100644 --- a/frontend/src/app/docs/api/ApiReferenceClient.tsx +++ b/frontend/src/app/docs/api/ApiReferenceClient.tsx @@ -370,15 +370,15 @@ function EndpointCard({ endpoint }: { endpoint: Endpoint }) { } const ERROR_CODES = [ - { code: '400', desc: 'Bad Request - Missing Idempotency-Key or invalid parameters' }, - { code: '401', desc: 'Unauthorized - Missing or invalid API key, or a missing or wrong ingest signature' }, - { code: '402', desc: 'Payment Required - No active subscription for the workspace' }, - { code: '403', desc: 'Forbidden - Key lacks the required scope, or tenant_id / X-Tenant-ID names a different workspace' }, - { code: '404', desc: "Not Found - Resource doesn't exist" }, - { code: '409', desc: 'Conflict - Idempotency-Key reused with different request bytes' }, - { code: '422', desc: 'Unprocessable - Request body failed validation' }, - { code: '429', desc: 'Too Many Requests - Rate limited' }, - { code: '500', desc: 'Internal Error - Contact support' }, + { code: '400', desc: 'Bad Request: Missing Idempotency-Key or invalid parameters' }, + { code: '401', desc: 'Unauthorized: Missing or invalid API key, or a missing or wrong ingest signature' }, + { code: '402', desc: 'Payment Required: No active subscription for the workspace' }, + { code: '403', desc: 'Forbidden: Key lacks the required scope, or tenant_id / X-Tenant-ID names a different workspace' }, + { code: '404', desc: "Not Found: Resource doesn't exist" }, + { code: '409', desc: 'Conflict: Idempotency-Key reused with different request bytes' }, + { code: '422', desc: 'Unprocessable: Request body failed validation' }, + { code: '429', desc: 'Too Many Requests: Rate limited' }, + { code: '500', desc: 'Internal Error: Contact support' }, ]; const RATE_LIMIT_HEADERS = [ diff --git a/frontend/src/app/docs/authentication/page.tsx b/frontend/src/app/docs/authentication/page.tsx index a253ba9f4..8f7746fde 100644 --- a/frontend/src/app/docs/authentication/page.tsx +++ b/frontend/src/app/docs/authentication/page.tsx @@ -134,7 +134,7 @@ export default function AuthenticationDocsPage() {

Scopes limit which of the endpoints above a key can call. They are not a read-only guarantee: some other operations on the RegEngine backend, for example compliance snapshots, SLA request updates, onboarding steps - and the EPCIS intake endpoint (in Preview), accept any valid tenant key without checking a named scope. Treat every key as + and the EPCIS intake endpoint (Preview), accept any valid tenant key without checking a named scope. Treat every key as read-write for its workspace. Keep it on your server, never in client-side code, and ask RegEngine to revoke it if it leaks.

diff --git a/frontend/src/app/docs/walkthrough/page.tsx b/frontend/src/app/docs/walkthrough/page.tsx index 5134e3e7f..ead554729 100644 --- a/frontend/src/app/docs/walkthrough/page.tsx +++ b/frontend/src/app/docs/walkthrough/page.tsx @@ -73,7 +73,7 @@ export default function WalkthroughPage() { {/* Step 1 */}

- Post a Receiving CTE for a lot that arrived at your facility — the starting anchor for + Post a Receiving CTE for a lot that arrived at your facility. It is the starting anchor for the chain. Fill the skeleton's {''},{' '} immediate_previous_source, and the other fields with your own values.

diff --git a/frontend/src/app/fsma-204/[cte]/page.tsx b/frontend/src/app/fsma-204/[cte]/page.tsx index dfade3342..b7c657e10 100644 --- a/frontend/src/app/fsma-204/[cte]/page.tsx +++ b/frontend/src/app/fsma-204/[cte]/page.tsx @@ -425,17 +425,9 @@ export default async function CTELandingPage({ title="How RegEngine Helps" description={ <> - {cta.enabled ? ( - <> - Upload your {data.name.toLowerCase()} data to the sandbox and - RegEngine surfaces these issues before they become audit findings during a §1.1455 records request. - - ) : ( - <> - RegEngine surfaces these issues in your {data.name.toLowerCase()}{" "} - data before they become audit findings during a §1.1455 records request. - - )} + Upload your {data.name.toLowerCase()} data to{" "} + RegEngine and + RegEngine surfaces these issues before they become audit findings during a §1.1455 records request. } > diff --git a/frontend/src/app/fsma-204/page.tsx b/frontend/src/app/fsma-204/page.tsx index 3b8e9a9e8..bda6844df 100644 --- a/frontend/src/app/fsma-204/page.tsx +++ b/frontend/src/app/fsma-204/page.tsx @@ -372,8 +372,6 @@ export default function FSMA204BusinessGuidePage() {

Try it:{" "} - /proof - {" "}→{" "} /verify.

diff --git a/frontend/src/app/fsma/page.tsx b/frontend/src/app/fsma/page.tsx index dd599e907..76d748a5d 100644 --- a/frontend/src/app/fsma/page.tsx +++ b/frontend/src/app/fsma/page.tsx @@ -95,8 +95,9 @@ function FSMADashboardContent() { } }; - const handleStartDrill = async (type: RecallDrillType = 'FORWARD', fallbackTlc = 'TLC1001') => { - const tlc = activeTlc || fallbackTlc; + const handleStartDrill = async (type: RecallDrillType = 'FORWARD') => { + const tlc = activeTlc; + if (!tlc) return; try { await createDrill.mutateAsync({ type, @@ -157,8 +158,8 @@ function FSMADashboardContent() { } > - Cookie-backed session auth now powers the live FSMA dashboard. Until you sign in, - demo visualization is shown below. + Cookie-backed session auth powers the live FSMA dashboard. Sign in to see + your live facilities, lots, and recall readiness below. )} @@ -223,14 +224,6 @@ function FSMADashboardContent() { Trace
- - {/* Demo Facilities Hint */} - {!activeTlc && ( -

- Try: LOT-2025-ROMAINE-001 or{' '} - TLC-LETTUCE-BATCH-42 -

- )} @@ -272,8 +265,16 @@ function FSMADashboardContent() { bare size="sm" icon={Shield} - title="No supply chain path yet" - description="Run a traceability query to draw the chain for a lot code." + title="No supply chain data connected yet" + description="Import your facilities and lots, then run a traceability query to draw the chain for a lot code." + action={ + + } /> )} @@ -323,7 +324,7 @@ function FSMADashboardContent() { drill={activeDrill} onCancel={() => { recallDrills.refetch(); }} onComplete={() => { recallDrills.refetch(); }} - onStartDrill={() => handleStartDrill('FORWARD')} + onStartDrill={activeTlc ? () => handleStartDrill('FORWARD') : undefined} /> {/* Compliance Readiness */} diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index d83358fa7..ca6031af9 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -391,9 +391,9 @@ body[data-inflow-lab="true"] { --re-shadow-glow: 0 0 0 3px var(--re-brand-muted); --re-shadow-glow-strong: 0 0 0 3px var(--re-brand-muted); - /* ── Type families. Marketing wears Outfit; the product wears Inter. ── */ + /* Type families. Marketing and product both wear Inter. */ --re-font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif; - --re-font-display: "Outfit", "Inter", system-ui, -apple-system, sans-serif; + --re-font-display: "Inter", system-ui, -apple-system, sans-serif; --re-font-serif: Georgia, "Times New Roman", serif; --re-font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace; diff --git a/frontend/src/app/ingest/page.tsx b/frontend/src/app/ingest/page.tsx index b30ade7ea..da905cdf3 100644 --- a/frontend/src/app/ingest/page.tsx +++ b/frontend/src/app/ingest/page.tsx @@ -664,7 +664,7 @@ export default function IngestPage() { htmlFor="epcis-file-upload" className="inline-flex w-fit items-center gap-1 text-ui font-medium text-re-text-primary" > - Select EPCIS file + Select EPCIS file (Preview) Preview diff --git a/frontend/src/app/integrations/page.tsx b/frontend/src/app/integrations/page.tsx index 7e677721e..52d77d7e5 100644 --- a/frontend/src/app/integrations/page.tsx +++ b/frontend/src/app/integrations/page.tsx @@ -22,6 +22,8 @@ import { type CustomerVisibleStatus, } from "@/lib/customer-readiness"; import { Badge, type BadgeTone } from "@/components/ui/badge"; +import { EmptyState } from "@/components/ui/empty-state"; +import { NO_DATA_YET } from "@/lib/empty-values"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Container } from "@/components/layout/container"; @@ -74,6 +76,7 @@ const STATUS_TONE: Record = { implemented: "success", supported: "success", pilot: "info", + preview: "info", design_partner: "info", export_supported: "neutral", file_import_supported: "neutral", @@ -126,6 +129,22 @@ export default function IntegrationsPage() {
+ {grouped.length === 0 && ( + + Upload a CSV + + } + secondaryAction={ + + } + /> + )} {grouped.map((group) => { const CategoryIcon = group.icon; diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 9d8bc43e2..b1765c4e0 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -56,7 +56,7 @@ export default async function RootLayout({ {/* PWA manifest (#568) */} diff --git a/frontend/src/app/onboarding/bulk-upload/page.tsx b/frontend/src/app/onboarding/bulk-upload/page.tsx index 89f896b1b..04d34ef6e 100644 --- a/frontend/src/app/onboarding/bulk-upload/page.tsx +++ b/frontend/src/app/onboarding/bulk-upload/page.tsx @@ -480,7 +480,7 @@ export default function BulkUploadPage() { {csvPreview.sampleRows.length > 0 && (

- Sample Data (first {csvPreview.sampleRows.length} rows) + First {csvPreview.sampleRows.length} rows of your file

diff --git a/frontend/src/app/proof/opengraph-image.tsx b/frontend/src/app/proof/opengraph-image.tsx deleted file mode 100644 index 1d60dfe75..000000000 --- a/frontend/src/app/proof/opengraph-image.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { renderOgCard, OG_SIZE, OG_CONTENT_TYPE } from '@/lib/og-image'; - -export const runtime = 'edge'; -export const alt = 'Verifiable evidence, not screenshots'; -export const size = OG_SIZE; -export const contentType = OG_CONTENT_TYPE; - -export default function Image() { - return renderOgCard({ eyebrow: 'Proof', title: 'Verifiable evidence, not screenshots' }); -} diff --git a/frontend/src/app/proof/transformation/opengraph-image.tsx b/frontend/src/app/proof/transformation/opengraph-image.tsx deleted file mode 100644 index 78ee3ea14..000000000 --- a/frontend/src/app/proof/transformation/opengraph-image.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { renderOgCard, OG_SIZE, OG_CONTENT_TYPE } from '@/lib/og-image'; - -export const runtime = 'edge'; -export const alt = 'CSV to CTE to FDA row, verified end to end'; -export const size = OG_SIZE; -export const contentType = OG_CONTENT_TYPE; - -export default function Image() { - return renderOgCard({ eyebrow: 'Proof', title: 'CSV to CTE to FDA row, verified end to end' }); -} diff --git a/frontend/src/app/proof/transformation/page.tsx b/frontend/src/app/proof/transformation/page.tsx index 601e1286f..88330c0f3 100644 --- a/frontend/src/app/proof/transformation/page.tsx +++ b/frontend/src/app/proof/transformation/page.tsx @@ -1,80 +1,6 @@ -import type { Metadata } from "next"; -import Link from "next/link"; -import { ArrowRight } from "lucide-react"; -import { Container } from "@/components/layout/container"; -import { PageHeader } from "@/components/layout/page-header"; -import { Section } from "@/components/layout/section"; -import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; +import { notFound } from 'next/navigation'; -export const metadata: Metadata = { - title: "Proof: CSV to FDA row | RegEngine", - description: - "How a supplier CSV becomes an FDA-oriented sortable record in RegEngine. No worked example is published yet.", - openGraph: { - title: "How a supplier CSV becomes an FDA-oriented sortable record | RegEngine", - description: "No worked example is published yet.", - url: "https://regengine.co/proof/transformation", - type: "website", - }, -}; - -export default function TransformationProofPage() { - return ( -
- {/* Hero */} -
- - - ← Proof - - -

- The pipeline is: raw supplier CSV, canonical TraceabilityEvent, then - the FDA-oriented sortable row. The canonical event shape lives in{" "} - - services/shared/canonical_event.py - {" "} - and the export columns in{" "} - - services/ingestion/app/fda_export_service.py - - . -

-
-
- -
- -

Worked example

-

No data yet

-

- There is no worked example with real data to show here. Run the pipeline on your - own records and verify the resulting export bundle yourself. -

-
- - -
-
-
-
- ); +// Hidden: the walkthrough ran on synthetic CSV rows. Restore with a real export. +export default function TransformationPage() { + notFound(); } diff --git a/frontend/src/app/records/page.tsx b/frontend/src/app/records/page.tsx index 34949ab20..6feee6e24 100644 --- a/frontend/src/app/records/page.tsx +++ b/frontend/src/app/records/page.tsx @@ -36,8 +36,8 @@ const SOURCE_LABEL: Record = { webhook_api: 'Webhook API', csv_upload: 'CSV Upload', xlsx_upload: 'XLSX Upload', - epcis_api: 'EPCIS API', - epcis_xml: 'EPCIS XML', + epcis_api: 'EPCIS API (Preview)', + epcis_xml: 'EPCIS XML (Preview)', manual: 'Manual', legacy_v002: 'Legacy', }; diff --git a/frontend/src/app/requests/page.tsx b/frontend/src/app/requests/page.tsx index e4194bedb..a4236ed9a 100644 --- a/frontend/src/app/requests/page.tsx +++ b/frontend/src/app/requests/page.tsx @@ -94,7 +94,7 @@ export default function RequestWorkflowPage() { const pageHeader = ( {ruleList.length} rules : undefined} primaryAction={!rules.error ? ( + } + />

Bundle

diff --git a/frontend/src/app/settings/support/page.tsx b/frontend/src/app/settings/support/page.tsx index 615d1b63b..caa786d18 100644 --- a/frontend/src/app/settings/support/page.tsx +++ b/frontend/src/app/settings/support/page.tsx @@ -3,6 +3,7 @@ import Link from 'next/link'; import { ShieldAlert, FileText, Mail } from 'lucide-react'; import { SUPPORT_CHANNELS } from '@/lib/customer-readiness'; +import { NOT_SET } from '@/lib/empty-values'; import { PageContainer } from '@/components/layout/page-container'; import { PageHeader } from '@/components/layout/page-header'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; @@ -27,13 +28,13 @@ export default function SupportSettingsPage() {
- {channel.tier} - {channel.responseWindow} + {channel.tier || NOT_SET} + {channel.responseWindow || NOT_SET}
-

{channel.escalation}

-

{channel.notes}

+

{channel.escalation || NOT_SET}

+

{channel.notes || NOT_SET}

))} diff --git a/frontend/src/app/sitemap.ts b/frontend/src/app/sitemap.ts index a34ac1c5e..185a4aba2 100644 --- a/frontend/src/app/sitemap.ts +++ b/frontend/src/app/sitemap.ts @@ -94,7 +94,6 @@ export default function sitemap(): MetadataRoute.Sitemap { const contentPages = [ { url: `${baseUrl}/fsma-204`, changeFrequency: 'monthly' as const, priority: 0.9 }, { url: `${baseUrl}/walkthrough`, changeFrequency: 'monthly' as const, priority: 0.9 }, - { url: `${baseUrl}/case-studies`, changeFrequency: 'monthly' as const, priority: 0.8 }, { url: `${baseUrl}/developers`, changeFrequency: 'monthly' as const, priority: 0.8 }, { url: `${baseUrl}/blog`, changeFrequency: 'weekly' as const, priority: 0.8 }, // Every published post, from the same list the blog index renders — @@ -127,8 +126,6 @@ export default function sitemap(): MetadataRoute.Sitemap { { url: `${baseUrl}/trust/architecture`, changeFrequency: 'monthly' as const, priority: 0.5 }, { url: `${baseUrl}/trust/retention`, changeFrequency: 'monthly' as const, priority: 0.5 }, { url: `${baseUrl}/trust/support`, changeFrequency: 'monthly' as const, priority: 0.5 }, - { url: `${baseUrl}/proof`, changeFrequency: 'monthly' as const, priority: 0.5 }, - { url: `${baseUrl}/proof/transformation`, changeFrequency: 'monthly' as const, priority: 0.5 }, ]; // No blanket lastModified: stamping every URL "now" on every request diff --git a/frontend/src/app/tools/notice-validator/page.tsx b/frontend/src/app/tools/notice-validator/page.tsx index f6f872618..0d67d7b17 100644 --- a/frontend/src/app/tools/notice-validator/page.tsx +++ b/frontend/src/app/tools/notice-validator/page.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from 'react'; import Link from 'next/link'; -import { AlertTriangle, ArrowRight, CheckCircle2, ClipboardPaste, FileWarning, Info, RotateCcw, XCircle } from 'lucide-react'; +import { AlertTriangle, ArrowRight, CheckCircle2, Info, RotateCcw, XCircle } from 'lucide-react'; import { LeadGate } from '@/components/lead-gate/LeadGate'; import { EmailGate } from '@/components/tools/EmailGate'; import { Container } from '@/components/layout/container'; @@ -71,39 +71,6 @@ const REQUIREMENTS: Requirement[] = [ }, ]; -const SAMPLE_NOTICES = { - good: `FDA Request Response - -Traceability Lot Code: 00012345678901-LOT-2026-001 -Product Description: Romaine Lettuce - -Covered Critical Tracking Events: -- Receiving at FreshLeaf Distribution Center on 2026-03-03 -- Transformation into packed case lots on 2026-03-04 -- Shipping to retailer DC on 2026-03-05 - -Key Data Elements Included: -- Quantity: 500 cases -- Unit of Measure: cases -- Ship From: Valley Fresh Farms -- Ship To: FreshLeaf Distribution Center / Retailer DC-04 -- Event Dates and facility references - -Export Package: -- Sortable spreadsheet CSV attached -- Sortable spreadsheet prepared per §1.1455(c) 24 hour availability requirement -- SHA256 and chain hash references included for record verification`, - bad: `Lot response attached. - -Product moved through the system last week. - -Please review the records.`, -}; - -// Note: the "good" sample above is illustrative only — passing this self-check on a draft -// does not mean the draft satisfies §1.1455(c); the rule requires actual record content, -// not keyword presence. - export default function NoticeValidatorPage() { const [noticeText, setNoticeText] = useState(''); const [hasAnalyzed, setHasAnalyzed] = useState(false); @@ -162,22 +129,6 @@ export default function NoticeValidatorPage() {
- -
diff --git a/frontend/src/app/trust/support/page.tsx b/frontend/src/app/trust/support/page.tsx index 0c8b31f31..8c059d3cc 100644 --- a/frontend/src/app/trust/support/page.tsx +++ b/frontend/src/app/trust/support/page.tsx @@ -11,6 +11,7 @@ import { Zap, } from 'lucide-react'; import { SUPPORT_CHANNELS } from '@/lib/customer-readiness'; +import { NOT_SET } from '@/lib/empty-values'; import { Container } from '@/components/layout/container'; import { PageHeader } from '@/components/layout/page-header'; import { Section } from '@/components/layout/section'; @@ -143,13 +144,13 @@ export default function SupportPage() { {SUPPORT_CHANNELS.map((channel) => (
-
{channel.tier}
+
{channel.tier || NOT_SET}
- {channel.responseWindow} + {channel.responseWindow || NOT_SET}
-

{channel.escalation}

-

{channel.notes}

+

{channel.escalation || NOT_SET}

+

{channel.notes || NOT_SET}

))} diff --git a/frontend/src/app/walkthrough/page.tsx b/frontend/src/app/walkthrough/page.tsx index 9c9aa4939..dba6e898b 100644 --- a/frontend/src/app/walkthrough/page.tsx +++ b/frontend/src/app/walkthrough/page.tsx @@ -265,10 +265,6 @@ export default function WalkthroughPage() { See Security Details
-

- See the underlying transformation step by step at{' '} - /proof/transformation. -

diff --git a/frontend/src/app/why-regengine/page.tsx b/frontend/src/app/why-regengine/page.tsx index f917c5c55..090a23bd7 100644 --- a/frontend/src/app/why-regengine/page.tsx +++ b/frontend/src/app/why-regengine/page.tsx @@ -579,7 +579,7 @@ HTTP 201 {/* EPCIS */} -
+

EPCIS 2.0 export (Preview) @@ -588,7 +588,7 @@ HTTP 201 EPCIS 2.0 (Preview) can support trading-partner interoperability. RegEngine treats EPCIS 2.0 as a preview schema where the enabled workflow and source data allow it.

- Verify EPCIS coverage with each vendor; some platforms layer EPCIS over legacy schemas, which can introduce conversion edges. + Verify EPCIS (Preview) coverage with each vendor; some platforms layer EPCIS over legacy schemas, which can introduce conversion edges.

    {[ @@ -604,27 +604,6 @@ HTTP 201 ))}

- - - {` - - - - ObjectEvent - - - - urn:epc:id:sgtin:... - - - OBSERVE - - urn:epcglobal:cbv:bizstep:... - - -`} - -
{/* SMB Focus */} diff --git a/frontend/src/components/dashboard/metrics-overview-widget.tsx b/frontend/src/components/dashboard/metrics-overview-widget.tsx index 700b06393..da5d5b495 100644 --- a/frontend/src/components/dashboard/metrics-overview-widget.tsx +++ b/frontend/src/components/dashboard/metrics-overview-widget.tsx @@ -86,7 +86,7 @@ export function MetricsOverviewWidget({
{metricsError && (

- Refresh failed — showing the last loaded figures.{" "} + Refresh failed. Showing the last loaded figures.{" "}