Repository navigation
fix(suppliers): lock agreed sending hours to tenant admins - #3584
PetrefiedThunder merged 12 commits into
Conversation
…metric Adds supplier_facilities.agreed_sending_hours (nullable, positive) with Alembic and Supabase migrations and downgrades, a tenant scoped PUT route and dashboard control, and computes the late CTE metric against the agreed sending time. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
…CSRF mutation Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
…r-agreed-sending-hours; move Supabase migration to 20261024080000 Alembic b005ddbf1dba already revises main head 41f96588d87c. The Supabase file moves from 20261024070000 to 20261024080000 so it no longer collides with Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> #3576 and sorts after 20261024060000 (data_gap_time_to_fix); #3576 takes 20261024090000.
* fix(dashboard): drop fabricated demo data from recall report page Remove the hardcoded demo investigation stats and affected-lot rows (invented suppliers, lot codes, consumer counts). Demo mode now renders the same No data yet empty state with a link to import records. * feat(copy): label every EPCIS intake and export mention as (Preview) EPCIS is preview on both paths, so feature-name mentions across app and marketing copy now carry the (Preview) marker. The integrations eyebrow now reads CSV upload, JSON events API, EPCIS (Preview). The FDA sortable export stays unlabeled. Stored keys and option values are unchanged. * fix(copy): keep typed EPCIS format value unlabeled in customer-readiness The format field is a string union used for logic; the (Preview) marker stays on the display name only. * feat(pricing): show honest unavailable state when billing is unreachable Drop the hardcoded tier prices and the silent fallback. Prices now come only from the billing service; when it cannot be reached the page renders a Pricing is temporarily unavailable state with signup and contact links instead of invented figures. Hardcoded dollar amounts in the pricing copy and metadata are removed as well. EPCIS 2.0 export carries the (Preview) label; the FDA sortable export stays unlabeled. * fix(app): remove sample and sandbox banners and the simulated ingest walkthrough Dashboard shell and DemoBanner now say No data yet with a link to connect real data on the Ingest page. The ingest page no longer fakes a job for the first example URL; it submits for real. The Sample: Daily FDA package archive entry is removed from the archive jobs list. Test assertions updated for the EPCIS (Preview) status labels. * fix(data-import): remove synthetic sample dataset simulation Delete SAMPLE_EVENTS, the handleLoadSample flow with its state and types, and the Load Sample Dataset / Run Simulation card. A regression test asserts the control is not rendered. Real CSV upload and the API guide are unchanged. * test(copy): assert csv-sftp display text never renders SFTP Display labels were already clean (CSV Import); this locks the invariant for the csv-sftp registry entry name, customer copy, and the csv_sftp_import delivery-mode label. Stored keys and ids are unchanged. * fix(receiving): use a comma in generated Receiving Dock location names New receiving submissions name the location 'Receiving Dock, <supplier>' instead of using a dash. No existing records are touched. * fix(inflow-lab): hide Inflow Lab links from dashboard, docs, ingest, integrations and product * fix(tools): remove Inflow Lab simulator, walkthrough sample payloads and recall drill demo data Drops tools/inflow-lab components, lib/api-samples ingest walkthrough JSON, synthetic tools/ask and tools/export copy, and invented proof/recall-drill numbers. Recall report shows the #3536 no_data empty state. * ci: trigger required gates Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --------- Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(ci): Engine Review Pass reads issue comment markers and allows pi, deepseek, devops Real PASS markers on #3553, #3547, #3530 and #3527 were ignored: pi was not an allowed engine, issue comment markers were never read, and devops/ branches failed as an invalid author engine. Also rerun on PR issue comments. Signed-off-by: Christopher Sellers <1@christophersellers.com> * ci(engine-review-pass): drop devops, add hermes, require exact full sha Signed-off-by: Christopher Sellers <1@christophersellers.com> --------- Signed-off-by: Christopher Sellers <1@christophersellers.com>
… ratchet (#3549) * feat(copy): SEL-452 copy gate with four-status capabilities registry and baseline ratchet Signed-off-by: Christopher Sellers <1@christophersellers.com> * ci(copy): copy-gate path filters include copy-gate.yml Signed-off-by: Christopher Sellers <1@christophersellers.com> * fix(copy): gate against main's baseline so a PR cannot loosen the ratchet Signed-off-by: Christopher Sellers <1@christophersellers.com> * fix(copy-gate): reject unparseable registry, fail on baseline lookup errors, diff pushes against event.before * chore(copy-lint): refresh baseline after main removed sandbox copy (SEL-452) Signed-off-by: Christopher Sellers <1@christophersellers.com> * chore(copy-gate): refresh copy baseline after merging main (counts only dropped) * fix(schema): include public.data_gap_events in expected schema snapshot Regenerate docs/schema/expected_schema.json from a fresh alembic upgrade head so the RLS isolation suite schema parity check stops reporting EXTRA TABLE for the v135 data gap migration. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: Christopher Sellers <1@christophersellers.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
…p walkthrough dash Refresh copy-lint baseline.json so it matches current counts. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
…to claude/tenant-locked-sending-hours Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
Suppliers no longer set their own late CTE threshold. The write moves to
PUT /v1/supplier/admin/facilities/{facility_id}/agreed-sending-hours,
gated by the tenant Owner or Admin role check from tenant settings (#1386)
and scoped to facilities in the caller's tenant. The supplier dashboard
shows the agreed time read only.
Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review: 1
Please check the settings in the CodeRabbit UI or the
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
REVIEW: FAIL deepseek 0e6854d Reviewed by hand: Mac load was 28.7–29.9, over the 28 limit, so DeepSeek was not run. Delta is 61bb729..0e6854d, 8 files. What's right:
Blocking:
Not blocking: there is no route where a caller from another tenant gets 403 instead of 404 for an existing facility. Missing or wrong-role callers get 403 before any lookup, so a 403 doesn't reveal whether a facility exists. CI at this head: Copy gate, Security path changes and others are still pending. Head SHA Check Guard and PR Blocker Checklist failed (review gates). |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0e6854d. Configure here.
| onSaved, | ||
| }: { | ||
| facilityId: string; | ||
| hours: number | null; |
There was a problem hiding this comment.
Tenant page cannot edit sending hours
High Severity
SendingHoursNote makes agreed sending hours read-only with Set by your customer on the tenant Supplier Management page. Owner and Admin users are now the only roles that can write agreed_sending_hours, but they have no in-app editor, so the late CTE threshold cannot be set or cleared from the product.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 0e6854d. Configure here.
…dmin membership Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
|
REVIEW: PASS deepseek 361138c Delta review against my FAIL at 0e6854d (2 files). The Mac's load was 18.7–24.2, so I ran the tests and the manifest check myself in a scratch venv (/tmp/dsre84v, Python 3.12.13,
|
233b840
into
claude/supplier-data-quality
PR Blocker ChecklistHead SHA: Status: Blocked (1 open blocker)Review Threads (1 unresolved)
Changes Requested (0)
Blocked Engines (0)
|
Keeps the tenant admin lock from #3584 (admin only PUT, read only supplier view), runtime emails from #3587, and the tenant wide EPCIS (Preview) receipt metric from this PR. The epcis_failed label now says it counts across the whole account. Route manifest drops the old supplier PUT. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
* feat(suppliers): track data gap time to fix per supplier Open one gap event per flagged missing KDE or failed CTE validation, close it only when a passing record for the same lot and CTE arrives, and store a receipt hash of the closing record. Adds a per supplier view with open gaps, median time to fix, oldest open gap and a 24 hour at risk flag citing 21 CFR 1.1455(c)(1). Migration included but not applied. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * feat(alembic): add v135 data_gap_events revision and redact gap write errors Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * feat(suppliers): flag gaps open at a records request response due time Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * feat(suppliers): add per supplier data quality view Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * test(suppliers): generate ids at runtime in data quality tests Replace hand written UUIDs, record ids, lot codes and facility ids with uuid4 and crypto.randomUUID values, and assert on values derived from the generated inputs. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * test(suppliers): derive data quality test counts from generated inputs Counts now come from lengths of generated id arrays or from MIN_RECORDS, DEFAULT_WINDOW_DAYS and MAX_WINDOW_DAYS. KDE values, reasons, tenant and user ids are generated. Removes the Date.now based numerator. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * feat(suppliers): agreed sending hours and live late CTE metric (#3575) * feat(suppliers): agreed sending hours per facility and live late CTE metric Adds supplier_facilities.agreed_sending_hours (nullable, positive) with Alembic and Supabase migrations and downgrades, a tenant scoped PUT route and dashboard control, and computes the late CTE metric against the agreed sending time. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * test(security): count the agreed sending hours PUT as a dual session CSRF mutation Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * fix(copy): real-data follow-up to #3471 (v3) (#3544) * fix(dashboard): drop fabricated demo data from recall report page Remove the hardcoded demo investigation stats and affected-lot rows (invented suppliers, lot codes, consumer counts). Demo mode now renders the same No data yet empty state with a link to import records. * feat(copy): label every EPCIS intake and export mention as (Preview) EPCIS is preview on both paths, so feature-name mentions across app and marketing copy now carry the (Preview) marker. The integrations eyebrow now reads CSV upload, JSON events API, EPCIS (Preview). The FDA sortable export stays unlabeled. Stored keys and option values are unchanged. * fix(copy): keep typed EPCIS format value unlabeled in customer-readiness The format field is a string union used for logic; the (Preview) marker stays on the display name only. * feat(pricing): show honest unavailable state when billing is unreachable Drop the hardcoded tier prices and the silent fallback. Prices now come only from the billing service; when it cannot be reached the page renders a Pricing is temporarily unavailable state with signup and contact links instead of invented figures. Hardcoded dollar amounts in the pricing copy and metadata are removed as well. EPCIS 2.0 export carries the (Preview) label; the FDA sortable export stays unlabeled. * fix(app): remove sample and sandbox banners and the simulated ingest walkthrough Dashboard shell and DemoBanner now say No data yet with a link to connect real data on the Ingest page. The ingest page no longer fakes a job for the first example URL; it submits for real. The Sample: Daily FDA package archive entry is removed from the archive jobs list. Test assertions updated for the EPCIS (Preview) status labels. * fix(data-import): remove synthetic sample dataset simulation Delete SAMPLE_EVENTS, the handleLoadSample flow with its state and types, and the Load Sample Dataset / Run Simulation card. A regression test asserts the control is not rendered. Real CSV upload and the API guide are unchanged. * test(copy): assert csv-sftp display text never renders SFTP Display labels were already clean (CSV Import); this locks the invariant for the csv-sftp registry entry name, customer copy, and the csv_sftp_import delivery-mode label. Stored keys and ids are unchanged. * fix(receiving): use a comma in generated Receiving Dock location names New receiving submissions name the location 'Receiving Dock, <supplier>' instead of using a dash. No existing records are touched. * fix(inflow-lab): hide Inflow Lab links from dashboard, docs, ingest, integrations and product * fix(tools): remove Inflow Lab simulator, walkthrough sample payloads and recall drill demo data Drops tools/inflow-lab components, lib/api-samples ingest walkthrough JSON, synthetic tools/ask and tools/export copy, and invented proof/recall-drill numbers. Recall report shows the #3536 no_data empty state. * ci: trigger required gates Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --------- Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(ci): Engine Review Pass picks up real PASS markers (#3577) * fix(ci): Engine Review Pass reads issue comment markers and allows pi, deepseek, devops Real PASS markers on #3553, #3547, #3530 and #3527 were ignored: pi was not an allowed engine, issue comment markers were never read, and devops/ branches failed as an invalid author engine. Also rerun on PR issue comments. Signed-off-by: Christopher Sellers <1@christophersellers.com> * ci(engine-review-pass): drop devops, add hermes, require exact full sha Signed-off-by: Christopher Sellers <1@christophersellers.com> --------- Signed-off-by: Christopher Sellers <1@christophersellers.com> * feat(copy): SEL-452 copy gate with capabilities registry and baseline ratchet (#3549) * feat(copy): SEL-452 copy gate with four-status capabilities registry and baseline ratchet Signed-off-by: Christopher Sellers <1@christophersellers.com> * ci(copy): copy-gate path filters include copy-gate.yml Signed-off-by: Christopher Sellers <1@christophersellers.com> * fix(copy): gate against main's baseline so a PR cannot loosen the ratchet Signed-off-by: Christopher Sellers <1@christophersellers.com> * fix(copy-gate): reject unparseable registry, fail on baseline lookup errors, diff pushes against event.before * chore(copy-lint): refresh baseline after main removed sandbox copy (SEL-452) Signed-off-by: Christopher Sellers <1@christophersellers.com> * chore(copy-gate): refresh copy baseline after merging main (counts only dropped) * fix(schema): include public.data_gap_events in expected schema snapshot Regenerate docs/schema/expected_schema.json from a fresh alembic upgrade head so the RLS isolation suite schema parity check stops reporting EXTRA TABLE for the v135 data gap migration. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: Christopher Sellers <1@christophersellers.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(schema): snapshot supplier_facilities.agreed_sending_hours (v136) Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * fix(copy): label EPCIS as Preview in supplier quality and ingest, drop walkthrough dash Refresh copy-lint baseline.json so it matches current counts. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --------- Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Signed-off-by: Christopher Sellers <1@christophersellers.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(suppliers): lock agreed sending hours to tenant admins (#3584) * feat(suppliers): agreed sending hours per facility and live late CTE metric Adds supplier_facilities.agreed_sending_hours (nullable, positive) with Alembic and Supabase migrations and downgrades, a tenant scoped PUT route and dashboard control, and computes the late CTE metric against the agreed sending time. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * test(security): count the agreed sending hours PUT as a dual session CSRF mutation Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * fix(copy): real-data follow-up to #3471 (v3) (#3544) * fix(dashboard): drop fabricated demo data from recall report page Remove the hardcoded demo investigation stats and affected-lot rows (invented suppliers, lot codes, consumer counts). Demo mode now renders the same No data yet empty state with a link to import records. * feat(copy): label every EPCIS intake and export mention as (Preview) EPCIS is preview on both paths, so feature-name mentions across app and marketing copy now carry the (Preview) marker. The integrations eyebrow now reads CSV upload, JSON events API, EPCIS (Preview). The FDA sortable export stays unlabeled. Stored keys and option values are unchanged. * fix(copy): keep typed EPCIS format value unlabeled in customer-readiness The format field is a string union used for logic; the (Preview) marker stays on the display name only. * feat(pricing): show honest unavailable state when billing is unreachable Drop the hardcoded tier prices and the silent fallback. Prices now come only from the billing service; when it cannot be reached the page renders a Pricing is temporarily unavailable state with signup and contact links instead of invented figures. Hardcoded dollar amounts in the pricing copy and metadata are removed as well. EPCIS 2.0 export carries the (Preview) label; the FDA sortable export stays unlabeled. * fix(app): remove sample and sandbox banners and the simulated ingest walkthrough Dashboard shell and DemoBanner now say No data yet with a link to connect real data on the Ingest page. The ingest page no longer fakes a job for the first example URL; it submits for real. The Sample: Daily FDA package archive entry is removed from the archive jobs list. Test assertions updated for the EPCIS (Preview) status labels. * fix(data-import): remove synthetic sample dataset simulation Delete SAMPLE_EVENTS, the handleLoadSample flow with its state and types, and the Load Sample Dataset / Run Simulation card. A regression test asserts the control is not rendered. Real CSV upload and the API guide are unchanged. * test(copy): assert csv-sftp display text never renders SFTP Display labels were already clean (CSV Import); this locks the invariant for the csv-sftp registry entry name, customer copy, and the csv_sftp_import delivery-mode label. Stored keys and ids are unchanged. * fix(receiving): use a comma in generated Receiving Dock location names New receiving submissions name the location 'Receiving Dock, <supplier>' instead of using a dash. No existing records are touched. * fix(inflow-lab): hide Inflow Lab links from dashboard, docs, ingest, integrations and product * fix(tools): remove Inflow Lab simulator, walkthrough sample payloads and recall drill demo data Drops tools/inflow-lab components, lib/api-samples ingest walkthrough JSON, synthetic tools/ask and tools/export copy, and invented proof/recall-drill numbers. Recall report shows the #3536 no_data empty state. * ci: trigger required gates Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --------- Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(ci): Engine Review Pass picks up real PASS markers (#3577) * fix(ci): Engine Review Pass reads issue comment markers and allows pi, deepseek, devops Real PASS markers on #3553, #3547, #3530 and #3527 were ignored: pi was not an allowed engine, issue comment markers were never read, and devops/ branches failed as an invalid author engine. Also rerun on PR issue comments. Signed-off-by: Christopher Sellers <1@christophersellers.com> * ci(engine-review-pass): drop devops, add hermes, require exact full sha Signed-off-by: Christopher Sellers <1@christophersellers.com> --------- Signed-off-by: Christopher Sellers <1@christophersellers.com> * feat(copy): SEL-452 copy gate with capabilities registry and baseline ratchet (#3549) * feat(copy): SEL-452 copy gate with four-status capabilities registry and baseline ratchet Signed-off-by: Christopher Sellers <1@christophersellers.com> * ci(copy): copy-gate path filters include copy-gate.yml Signed-off-by: Christopher Sellers <1@christophersellers.com> * fix(copy): gate against main's baseline so a PR cannot loosen the ratchet Signed-off-by: Christopher Sellers <1@christophersellers.com> * fix(copy-gate): reject unparseable registry, fail on baseline lookup errors, diff pushes against event.before * chore(copy-lint): refresh baseline after main removed sandbox copy (SEL-452) Signed-off-by: Christopher Sellers <1@christophersellers.com> * chore(copy-gate): refresh copy baseline after merging main (counts only dropped) * fix(schema): include public.data_gap_events in expected schema snapshot Regenerate docs/schema/expected_schema.json from a fresh alembic upgrade head so the RLS isolation suite schema parity check stops reporting EXTRA TABLE for the v135 data gap migration. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: Christopher Sellers <1@christophersellers.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(schema): snapshot supplier_facilities.agreed_sending_hours (v136) Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * fix(copy): label EPCIS as Preview in supplier quality and ingest, drop walkthrough dash Refresh copy-lint baseline.json so it matches current counts. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * fix(suppliers): lock agreed sending hours to tenant admins Suppliers no longer set their own late CTE threshold. The write moves to PUT /v1/supplier/admin/facilities/{facility_id}/agreed-sending-hours, gated by the tenant Owner or Admin role check from tenant settings (#1386) and scoped to facilities in the caller's tenant. The supplier dashboard shows the agreed time read only. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * fix(suppliers): scope facility lookup in query and test real tenant admin membership Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --------- Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Signed-off-by: Christopher Sellers <1@christophersellers.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * test(suppliers): generate test emails at runtime (#3587) Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * feat(epcis): document receipts and live EPCIS failure metric (#3576) * feat(suppliers): agreed sending hours per facility and live late CTE metric Adds supplier_facilities.agreed_sending_hours (nullable, positive) with Alembic and Supabase migrations and downgrades, a tenant scoped PUT route and dashboard control, and computes the late CTE metric against the agreed sending time. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * feat(epcis): record a receipt for every EPCIS document and live failure metric Adds fsma.epcis_document_receipts with forced RLS and tenant policies (Alembic and Supabase migrations with downgrades), writes an accepted or rejected receipt with a sanitized error summary from the EPCIS ingest routes, and computes the EPCIS failure metric. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * test(epcis): size receipt batches from MIN_RECORDS and check partial counts are ints Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * test(security): count the agreed sending hours PUT as a dual session CSRF mutation Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * fix(copy): real-data follow-up to #3471 (v3) (#3544) * fix(dashboard): drop fabricated demo data from recall report page Remove the hardcoded demo investigation stats and affected-lot rows (invented suppliers, lot codes, consumer counts). Demo mode now renders the same No data yet empty state with a link to import records. * feat(copy): label every EPCIS intake and export mention as (Preview) EPCIS is preview on both paths, so feature-name mentions across app and marketing copy now carry the (Preview) marker. The integrations eyebrow now reads CSV upload, JSON events API, EPCIS (Preview). The FDA sortable export stays unlabeled. Stored keys and option values are unchanged. * fix(copy): keep typed EPCIS format value unlabeled in customer-readiness The format field is a string union used for logic; the (Preview) marker stays on the display name only. * feat(pricing): show honest unavailable state when billing is unreachable Drop the hardcoded tier prices and the silent fallback. Prices now come only from the billing service; when it cannot be reached the page renders a Pricing is temporarily unavailable state with signup and contact links instead of invented figures. Hardcoded dollar amounts in the pricing copy and metadata are removed as well. EPCIS 2.0 export carries the (Preview) label; the FDA sortable export stays unlabeled. * fix(app): remove sample and sandbox banners and the simulated ingest walkthrough Dashboard shell and DemoBanner now say No data yet with a link to connect real data on the Ingest page. The ingest page no longer fakes a job for the first example URL; it submits for real. The Sample: Daily FDA package archive entry is removed from the archive jobs list. Test assertions updated for the EPCIS (Preview) status labels. * fix(data-import): remove synthetic sample dataset simulation Delete SAMPLE_EVENTS, the handleLoadSample flow with its state and types, and the Load Sample Dataset / Run Simulation card. A regression test asserts the control is not rendered. Real CSV upload and the API guide are unchanged. * test(copy): assert csv-sftp display text never renders SFTP Display labels were already clean (CSV Import); this locks the invariant for the csv-sftp registry entry name, customer copy, and the csv_sftp_import delivery-mode label. Stored keys and ids are unchanged. * fix(receiving): use a comma in generated Receiving Dock location names New receiving submissions name the location 'Receiving Dock, <supplier>' instead of using a dash. No existing records are touched. * fix(inflow-lab): hide Inflow Lab links from dashboard, docs, ingest, integrations and product * fix(tools): remove Inflow Lab simulator, walkthrough sample payloads and recall drill demo data Drops tools/inflow-lab components, lib/api-samples ingest walkthrough JSON, synthetic tools/ask and tools/export copy, and invented proof/recall-drill numbers. Recall report shows the #3536 no_data empty state. * ci: trigger required gates Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --------- Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(ci): Engine Review Pass picks up real PASS markers (#3577) * fix(ci): Engine Review Pass reads issue comment markers and allows pi, deepseek, devops Real PASS markers on #3553, #3547, #3530 and #3527 were ignored: pi was not an allowed engine, issue comment markers were never read, and devops/ branches failed as an invalid author engine. Also rerun on PR issue comments. Signed-off-by: Christopher Sellers <1@christophersellers.com> * ci(engine-review-pass): drop devops, add hermes, require exact full sha Signed-off-by: Christopher Sellers <1@christophersellers.com> --------- Signed-off-by: Christopher Sellers <1@christophersellers.com> * feat(copy): SEL-452 copy gate with capabilities registry and baseline ratchet (#3549) * feat(copy): SEL-452 copy gate with four-status capabilities registry and baseline ratchet Signed-off-by: Christopher Sellers <1@christophersellers.com> * ci(copy): copy-gate path filters include copy-gate.yml Signed-off-by: Christopher Sellers <1@christophersellers.com> * fix(copy): gate against main's baseline so a PR cannot loosen the ratchet Signed-off-by: Christopher Sellers <1@christophersellers.com> * fix(copy-gate): reject unparseable registry, fail on baseline lookup errors, diff pushes against event.before * chore(copy-lint): refresh baseline after main removed sandbox copy (SEL-452) Signed-off-by: Christopher Sellers <1@christophersellers.com> * chore(copy-gate): refresh copy baseline after merging main (counts only dropped) * fix(schema): include public.data_gap_events in expected schema snapshot Regenerate docs/schema/expected_schema.json from a fresh alembic upgrade head so the RLS isolation suite schema parity check stops reporting EXTRA TABLE for the v135 data gap migration. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: Christopher Sellers <1@christophersellers.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(schema): snapshot supplier_facilities.agreed_sending_hours (v136) Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> * fix(copy): label EPCIS as Preview in supplier quality and ingest, drop walkthrough dash Refresh copy-lint baseline.json so it matches current counts. Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> --------- Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Signed-off-by: Christopher Sellers <1@christophersellers.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: Christopher <131207452+PetrefiedThunder@users.noreply.github.com> Signed-off-by: Christopher Sellers <1@christophersellers.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>


Suppliers could set their own late CTE threshold through PUT /v1/supplier/facilities/{facility_id}/agreed-sending-hours. This moves the write to the tenant.
Changes
Tests (data free, runtime ids): tenant admin sets and clears for any facility in tenant; supplier roles (no membership, Member, Viewer, supplier) get 403 even on their own facility; old path is gone; admin of another tenant gets 404; validation and missing tenant cases. Frontend test asserts the read only view has no edit controls.
Local: admin supplier pytest pass, supplier-data-quality vitest 14/14, tsc only the existing instrumentation.ts error, copy gate exit 0. Manifest generator could not run locally (typesafe_sdk not installed); the manifest was edited by hand to match, CI --check will confirm.
Merge order: lands on claude/supplier-data-quality (#3574), after #3575 (already squashed in as 61bb729).
Note
Medium Risk
Authorization and API surface change for a compliance-related setting: suppliers lose write access and tenant admins gain cross-facility write within a tenant, which is correct but affects integrations using the old path.
Overview
Agreed sending hours (the late CTE threshold) can no longer be changed by suppliers. The write API moves from
PUT /v1/supplier/facilities/{facility_id}/agreed-sending-hourstoPUT /v1/supplier/admin/facilities/{facility_id}/agreed-sending-hours, gated byTenantAdminChecker(tenant.admin— tenant Owner or Admin). Facility updates are resolved by tenant id only (any facility in the tenant), not supplier ownership; cross-tenant facilities return 404.On the supplier dashboard, the editable
SendingHoursEditoris removed in favor of a read-only note (“Set by your customer”);onSendingHoursSaved/ reload wiring is dropped.api-clienttargets the new admin path. The route security manifest reflects the new operation and permission.Tests are updated for admin set/clear, 403 for non-admin roles (including facility owners), removal of the old path, cross-tenant 404, and an integration-style
TenantAdminCheckermembership check.Reviewed by Cursor Bugbot for commit 361138c. Bugbot is set up for automated code reviews on this repo. Configure here.