Skip to content

Unify admin CMS and harden Gemini OCR and storyboard execution - #3215

Merged
twoimo merged 41 commits into
developfrom
codex/feature-completion-20261013
Oct 19, 2026
Merged

twoimo merged 41 commits into
developfrom
codex/feature-completion-20261013

Conversation

@twoimo

@twoimo twoimo commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

관리자 콘솔의 헤더·사이드바·CMS 목록과 상세를 통일하고, OCR 결과가 불확실할 때 같은 영수증을 중복 실행하거나 검수가 정체되는 문제를 보완합니다. 처리 중·오래된 요청은 거부하며, 명시적 복구는 영속 operation과 입력 세대에 결속합니다.

변경:

  • 관리자15개 메뉴의 공통 구조·열 정렬·검색·상태·상세·키보드 포커스를 정리했습니다. 미저장 편집을 보호하고 사용자·배너 변경은 관측 revision으로 충돌을 처리합니다. OCR 확인창은 비용 경고와 확인에 필요한 문구만 표시합니다.
  • OCR 사진의 확정 결함은 전체 snapshot CAS와 독립 readback 후 terminal 처리합니다. 혼합 모델 응답·parsing 실패 이력이 있으면 credential 거절로 재분류하지 않고 uncertain fence를 유지합니다.
  • 일반 dispatch 재시도와 명시적 indeterminate 복구를 구분합니다. 같은 operation은 세대를 한 번만 바꾸며, 사진·image hash·모델 정책·thinking·버전 binding을 유지합니다. 활성 bound claim 동안 리뷰 삭제를 차단하고 undispatched release 또는 terminal 결과 뒤에 허용합니다.
  • 기존 migration은 보존합니다. 새107은 독립 owner/grantor/grantee/ACL admission,108은 실제 함수17개·테이블3개·트리거2개의 catalog equality와 정확한 ledger 봉인을 요구합니다. 각 successor의 운영 launch는 계속 held입니다.
  • PG15 source-only 재생은 exact raw hash에 고정된 admission block만 변환합니다. 운영 ledger·함수·FK·trigger·seal을 느슨하게 만들지 않습니다. 필수 CI·scorer·표본 수를 유지합니다.
  • 리뷰 음식 사진20장, 배너 제목100자 사전 차단 및 확정 거절에 한정한 현재 업로드 정리, 스토리보드35개 실행 경계와 롱폼 lineage를 유지합니다.

검증:

  • 앱 snapshot137 tests/1,011 assertions와 최종 fixture compiler3/59의 구성 검증140/1,070, Node24.22.0의 고정 TypeScript parity3,356입력/진단0, scoped ESLint, production build54/54 및 CSS 경계 통과.
  • 실제 component·loopback 합성 endpoint의 OCR UI39/39: 취소 시 apply0, ACK-loss와 재로드의 operation 재사용, 처리 중·stale 거부, 내부 설명·고정 ETA 제거. 외부/provider/hosted 호출0.
  • PG15 두 clean replay는 각1,960행·비교 hash58개 동등, 소유 container/volume/network exact cleanup·잔존0. Fixture-only 변경은 generator 입력232파일의 동일 hash와 별도 compiler 검사로 결속했습니다.
  • PostgreSQL17 canonical105→108·초기 ACL rollback·NOSUPERUSER/멤버십 복원·본문/트리거/FK drift 거부·실제 두 세션의 삭제 장벽을 확인했습니다. 최종 main fixture12/12도 통과했습니다. Public RPC25개는 service_role, private readback11개는 postgres이며 권한을 넓히지 않았습니다. Postgres의 기존 BYPASSRLS·CREATEROLE baseline flag는 유지합니다.
  • 기존 헤더45/45·배너 경계·음식 사진·storyboard 증거와 최초 실패는 보고서에 보존합니다. 겹치는 테스트 수를 합산하지 않으며 결정적 fixture나 단일 build 시간을 운영 성능·비용 개선으로 해석하지 않습니다.

운영 경계:

실제 Gemini 반대 감사와 최신 소스 역검증,89→108 운영 executor 연결·fresh writer fence·현재 production rollback/readback, 보호된 develop→data→main 승격·운영 SQL·배포·인증된15개 메뉴 전체 검증은 미완료입니다. 기존 연구 FIFO/job/hash와 운영 hold를 유지합니다. 로컬 통과는 hosted 적용이나 전체 기능 정상 작동의 증거가 아닙니다.

증빙: docs/operations/goal-resume-20261018.md, docs/operations/review-recovery-20261019.md, apps/web/performance/cross-audit-20261019/의 분리된 source·DB·browser·CI receipts 및 SHA-256.

@vercel

vercel Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
tzudong Ignored Ignored Preview Oct 18, 2026 9:00pm UTC

@gitguardian

gitguardian Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T21:05:07.536710Z 4ebf31f New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51414ef25b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/app/api/ocr/extract/route.ts Outdated
Comment thread apps/web/app/api/admin/ocr-receipts/process/route.ts Outdated
Comment thread apps/web/lib/admin/storyboard/outbound-worker.ts Outdated
Comment thread apps/web/components/reviews/ReviewModal.tsx Outdated
Comment thread apps/web/app/api/ocr/extract/route.ts
Comment thread apps/web/scripts/preview-design-fixtures.mjs Outdated
@twoimo twoimo changed the title Persist receipt OCR and enforce Gemini-only storyboard execution Unify admin CMS and harden Gemini OCR and storyboard execution Oct 15, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6160a87d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/knowledge_graph/longform_inventory.py Outdated
Comment thread backend/knowledge_graph/longform_inventory.py Outdated
Comment thread backend/knowledge_graph/longform_inventory.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c8b617539d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/lib/admin/storyboard/gemini-client.ts
Comment thread apps/web/lib/admin/storyboard/gemini-client.ts Outdated
Comment thread .github/workflows/ocr-review-receipts.yml Outdated
Comment thread apps/web/components/reviews/ReviewModal.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea08db3ae0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/utils/provider-budget.mjs Outdated
Comment thread apps/web/app/api/ocr/reviews/save/route.ts
Comment thread backend/knowledge_graph/longform_analysis.py Outdated
Comment thread apps/web/lib/admin/storyboard/runtime-source-binding.ts
Comment thread backend/knowledge_graph/caption_osk_adapter.py Outdated
Comment thread apps/web/components/reviews/ReviewModal.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 457023a656

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/app/api/ocr/extract/route.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86328a4c52

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/app/api/ocr/reviews/save/route.ts Outdated
Verify held evidence-prefixed SQL aggregates and retain source-bound browser and build evidence.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17eec51ae1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/lib/ocr/durable-result.ts
Comment thread apps/web/lib/admin/storyboard/runtime-source-binding.ts
Comment thread apps/web/lib/ocr/admin-receipt-batch.ts
Comment thread apps/web/app/admin/banners/page.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dfa77ca0b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/app/api/admin/ocr-receipts/rerun/route.ts Outdated
@twoimo

twoimo commented Oct 18, 2026

Copy link
Copy Markdown
Owner Author

Nightly coupling: this PR keeps the hardcoded ledger count of 140 and adds 8 migrations.

At head 4ebf31f6, backend/supabase/migrations has 151 .sql units (develop 716e36fc has 143). These pins still say 140:

  • backend/supabase/scripts/local-migrate.py:119 (EXPECTED_LEDGER_UNITS = 140)
  • .github/scripts/build-nightly-local-publication.py:20
  • .github/scripts/verify-nightly-local-publication.py:129
  • apps/web/scripts/run-nightly-regression.mjs:1326 (receipt.ledger.length !== 140)

Main's Nightly already fails on this pin for the 143-unit tree (run 39165648646 on b4eb780c, step Verify publication boundary implementation, receipt_ledger_state). PR CI does not run that step, so this PR is green while the gap grows.

Repro (box, Python 3.13.5). Run python3 -m unittest backend.supabase.tests.test_local_publication_verifier backend.supabase.tests.test_local_seed_receipt_contract backend.supabase.tests.test_review_media_catalog_integration backend.supabase.tests.test_local_function_runtime_contract.

Tree Result
this PR alone (4ebf31f6) 106 tests, failures=3, errors=40, AssertionError: 151 != 140
4ebf31f6 merged with #3310 head 19d0ac0e (clean merge) 107 tests, OK

#3310 derives the count from the migration list, so it covers these 8 new units with no extra edit. No change is needed in this PR if #3310 lands first or with it. If this PR merges alone, the next promotion to main keeps Nightly red with 151 != 140.

Not verified here: the Docker local-stack lanes (migration apply, receipt readback, E2E) for the 8 new units.

@twoimo

twoimo commented Oct 19, 2026

Copy link
Copy Markdown
Owner Author

Recovery rejects model policies that the normal OCR path accepts (head 4ebf31f6)

The recovery SQL caps the model policy at 3 entries and 120-character names. Every other layer accepts up to 8 entries and 160-character names. With a 4 to 8 model GEMINI_OCR_MODEL list, normal OCR runs, but a review whose run ends provider-indeterminate can never be recovered or rerun from the admin UI.

Where the bounds disagree:

What the admin sees with a 4-model policy:

  1. A normal run claims the result and calls the provider. If the provider outcome is uncertain, the row stays reset with one indeterminate/uncertain result.
  2. "OCR 복구" calls preview, which returns eligible. The UI then sends recover_provider_indeterminate.
  3. recover_review_receipt_ocr_indeterminate raises 22023. applyReceiptOcrIndeterminateRecovery maps the error to ReceiptOcrRecoveryPersistenceError, and the route returns 503 OCR_RECOVERY_READBACK_FAILED. The UI drops the retained operation because dispatchIndeterminate is false.
  4. A plain rerun of the same row returns 409 OCR_RECOVERY_REQUIRED, because preview still says eligible. Each later click repeats steps 2 and 3. The row stays stuck until someone shortens the deployed model list.

Evidence. I ran the predicates verbatim from this head in PGlite (Postgres compiled to WASM). I did not apply the full migration chain.

policy        valid_model_policy  recover_raises_22023  recovery_table_check
3 models      true                false                 true
4 models      true                true                  false
1 x 137-char  true                true                  true

What is known and what is not. The bound mismatch is true in the code at this head. I can't see the deployed GEMINI_OCR_MODEL, so I don't know whether production uses more than 3 models today. I found no comment, test, or provider-side limit that explains the 3-entry cap.

Fix direction. Remove the inline validator in recover_review_receipt_ocr_indeterminate and call ocr_private.valid_model_policy(p_model_policy) there. Use the same function in the model_policy CHECK, so a single definition sets the bound. The recovery108 SQL successor manifest (.github/receipt-ocr-recovery108-sql-successor.v1.json) is hash-bound, so regenerate it with the repo's script.

Regression test. Add a case to backend/supabase/tests/receipt_ocr_indeterminate_recovery.sql. Seed an indeterminate result whose policy has 4 models, then assert that recovery returns rotated and the recovery row persists. The test fails at this head and passes after the fix.

@twoimo

twoimo commented Oct 19, 2026

Copy link
Copy Markdown
Owner Author

Follow-up evidence for the recovery model-policy bound (head 4ebf31f6, unchanged).

Real-stack attempt. A full local-stack apply was tried first. local-stack.py reset stopped at readiness_timeout_analytics (known box limit: Logflare DB pool timeouts), so no migrations were applied. As a fallback, I ran the verbatim ocr_private.valid_model_policy, the recovery inline validator, and the model_policy CHECK expression on the stack's real supabase/postgres:15.8.1.085 container (PostgreSQL 15.8), in a rolled-back transaction. This was a narrow, isolated SQL run and not a full migration apply.

policy valid_model_policy recovery guard recovery CHECK
3 models true ok true
4 models true 22023 false
8 models true 22023 false
1 × 137-char name true 22023 true

This matches the earlier PGlite result.

Second opinion (Gemini 4 Argon, high reasoning, read-only, asked to refute). Verdict: AGREE.

  • Agreed: the limits are as stated. The recovery RPC caps at 1..3 models and 1..120 chars (20261019130000_receipt_ocr_indeterminate_recovery.sql:498-512), and the recovery table CHECK caps at 1..3 (:209-214). The normal path allows 1..8 and 1..160: valid_model_policy (20261013190000_durable_receipt_ocr_results.sql:250-274), workflow-policy.ts:13-30, runtime-config.ts:29-38, and durable-result.ts:359-365.
  • Refutation attempted and failed: nothing upstream caps the policy. getGeminiOcrModels and sanitizeCsv (gemini.ts:78-93) only split, trim, and dedupe. Argon also found a second trigger. Even with the 3-model default, OCR_ROUTING_MODE=manual plus a distinct OCR_MANUAL_MODEL gives a 4-model policy (runtime-config.ts:100-117).
  • Agreed on the stuck-state chain. Preview returns eligible without looking at model_policy. Recover then raises 22023, which admin-receipt-recovery.ts:197 turns into OCR_RECOVERY_APPLY_FAILED, and the route returns 503 OCR_RECOVERY_READBACK_FAILED (rerun/route.ts:319-324). A plain rerun returns 409 OCR_RECOVERY_REQUIRED (rerun/route.ts:182-198), and reset-all skips rows that are already reset (admin-receipt-reset.ts:81-83). The row stays stuck until the env policy shrinks to 3 models or fewer.
  • Nuance: the client-visible code is OCR_RECOVERY_READBACK_FAILED, even though the actual failure happens at apply, so the error label is misleading for this case.
  • Agreed on the fix. valid_model_policy is IMMUTABLE, so it can be used directly in the table CHECK. The recovery column is unindexed and is passed straight into claim_receipt_ocr_result_core, which already uses valid_model_policy, so nothing needs the tighter bound.

Fix direction (unchanged). Delete the inline bounds and use ocr_private.valid_model_policy in both the recovery CHECK and the RPC guard.

@twoimo
twoimo merged commit ba6bc18 into develop Oct 19, 2026
32 of 33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant