Skip to content

feat(worker): Phase 3c — port heavy writes (freeze/mint/analyze/generate/trust/ledger/seed)#41

Open
chitcommit wants to merge 1 commit into
feat/hono-phase-3b-domain-writesfrom
feat/hono-phase-3c-heavy-writes
Open

feat(worker): Phase 3c — port heavy writes (freeze/mint/analyze/generate/trust/ledger/seed)#41
chitcommit wants to merge 1 commit into
feat/hono-phase-3b-domain-writesfrom
feat/hono-phase-3c-heavy-writes

Conversation

@chitcommit
Copy link
Copy Markdown
Contributor

Summary

Ports the 8 remaining write routes that touch external services from Express to Hono. Stacks on #40#39#38#37#36#34#33. Base: feat/hono-phase-3b-domain-writes.

Routes

Method Path Express External call
POST /api/assets/:id/freeze routes.ts:125 ChittyMint freeze
POST /api/assets/:id/mint routes.ts:166 ChittyMint mint
POST /api/assets/:assetId/calculate-trust-score routes.ts:596 OpenAI gpt-4o
POST /api/evidence/:evidenceId/analyze routes.ts:379 OpenAI gpt-4o (vision)
POST /api/legal/generate-document routes.ts:557 OpenAI gpt-4o
POST /api/evidence-ledger/submit routes.ts:51 ChittyLedger submit
POST /api/evidence-ledger/:chittyId/verify routes.ts:92 ChittyLedger verify
POST /api/seed-demo (dev-only) routes.ts:39 none (Neon only)

External client matrix

Service Client Timeout Error mapping
OpenAI worker/src/clients/openai.ts (new) 3s (8s for generate-document) OpenAIConfigError → 503, OpenAIClientError → 502
ChittyMint worker/src/clients/chittymint.ts (new) 3s MintClientError → 502 mint_unavailable
ChittyLedger worker/src/clients/chittyledger.ts (extended) 3s LedgerClientError(404) → 404 not_found, else 502 ledger_unavailable

All external calls use AbortController with per-call timeouts; no retries (caller concern, matches Phase 2c).

Secrets required for prod

  • wrangler secret put OPENAI_API_KEY --env production — required before first OpenAI call. Handlers return 503 service_unavailable until set.
  • (Future) If ChittyMint / ChittyLedger require auth tokens, add CHITTYMINT_TOKEN / CHITTYLEDGER_TOKEN via wrangler secret put. Current Express path was unauthenticated public-API; preserved here.

OPENAI_API_KEY is declared as OPENAI_API_KEY?: string in worker/src/env.ts so it is never a vars entry. The value is never logged.

OpenAI test strategy

Handlers are exercised in integration tests with OPENAI_API_KEY unset — the assertion is the 503 service_unavailable mapping (real handler path, real Neon for ownership checks, real Hono routing). Live OpenAI invocation is deferred to staging smoke-tests because:

  1. Cost — receipt/document/asset-photo prompts plus vision images add up across CI runs.
  2. Key handling — exposing a billable key in CI is a separate hardening exercise.

This is genuine error-path coverage of the 503/502/ownership-404/UUID-400 surfaces, not a mock. Real success path is covered by the existing Express service in staging and will be re-validated post-deploy via curl smoke tests.

ChittyMint endpoint gap (known)

CHITTYMINT_URL defaults to https://mint.chitty.cc but the path shape (/v1/chain/freeze, /v1/chain/mint) inherited from the legacy api.chittycloud.com client has not been verified live. Integration tests exercise the 502 mint_unavailable path against an unreachable host (real network error, real timeout). Real-success coverage requires either:

  • Wiring the mint endpoints at mint.chitty.cc (preferred), or
  • Adding a Cloudflare service binding to a dedicated chittymint Worker (Phase 4).

Asset state is not mutated when the upstream call fails — validated by the 502 tests that re-read chitty_chain_status post-failure and assert it is unchanged.

ChittyID minting deferral — NOT resolved here

This PR does not resolve Phase 3a's chitty_id=NULL deferral. Re-reading Express:

  • POST /api/assets (Express:265) calls services.id.generate('asset') and stores chittyId at create time.
  • POST /api/assets/:id/mint (Express:181) calls services.chain.mint(asset.chittyId, ...) and stores transactionHash in blockchainHash.

So /mint mints an evidence token on chain, not a ChittyID. Create-time ChittyID generation remains deferred to a separate phase that adds a ChittyID client hitting id.chitty.cc.

Express divergences

  1. trust-score backing. Express's /calculate-trust-score calls aiAnalysisService.calculateTrustScore (OpenAI), not ChittyTrust. The migration spec said "ChittyTrust call" but Express truth is OpenAI — Express semantics preserved verbatim.
  2. trust_score clamp. OpenAI returns 0–100 float; the assets.trust_score column is numeric(3,1) (max 99.9). Output is clamped to [0, 99.9] before UPDATE. Documented inline.
  3. /mint gate. Matches Express's chittyChainStatus === 'frozen' check only; no 7-day-elapsed check (aspirational in spec, not present in Express).
  4. /seed-demo chittyId. Express generates ChittyIDs eagerly via chittyCloudMcp.generateChittyId(). Worker version leaves chittyId NULL to match Phase 3a's deferral pattern — seeded rows still get minted via the standard /mint flow.
  5. /seed-demo env gate. New 403 gate when ENVIRONMENT !== "development". Express had no such gate (anyone authenticated could seed).

Validation evidence

Typecheck

npm run check: 0 new errors in worker/ (pre-existing server/ errors unchanged). Worker code compiles clean.

Tests — real Neon, NO MOCKS

Ran the full worker integration suite on ephemeral Neon branch phase-3c-test-5g5h (project steep-cloud-28172078, branch br-wandering-sound-akrytlv6):

Test Files  10 passed (10)
     Tests  94 passed (94)
  Duration  21.17s

New file worker/__tests__/heavy-writes.integration.test.ts adds 30 tests covering:

  • 400 bad-UUID / invalid-input across all 8 routes
  • 401 no-auth gates
  • 404 intruder / not-owned across freeze/mint/trust/analyze/legal-doc
  • 502 upstream-unreachable for ChittyMint (freeze + mint) and ChittyLedger (submit + verify)
  • 503 service_unavailable for missing OPENAI_API_KEY across trust-score/analyze/legal-doc
  • 400 invalid_state gate for /mint against unfrozen asset
  • Real call against live ledger.chitty.cc for nonexistent canonical ID — accepts 404 or 502 (both are real)
  • 403 dev-only gate for /seed-demo in production env
  • 201 + state validation for /seed-demo in development env

Fixture suffixes 5G / 5H avoid collision with 5A..5L from prior phases.

Wrangler dry-run

npx wrangler deploy --dry-run --env production
Total Upload: 624.80 KiB / gzip: 124.87 KiB
--dry-run: exiting now.

All bindings present (Hyperdrive, Assets, tail to chittytrack, all env vars).

Neon MCP state-change validation (BEGIN/ROLLBACK)

Executed via run_sql_transaction against the test branch — full freeze → mint state-machine plus trust-score update, all rolled back:

  • Asset insert: { id: 34a4fe2d-..., chitty_chain_status: 'draft', trust_score: '0.0' }
  • After trust-score update: { trust_score: '87.4' }
  • After freeze: { chitty_chain_status: 'frozen', ipfs_hash: 'QmValidationHash' }
  • After mint: { chitty_chain_status: 'minted', blockchain_hash: '0xValidationTxHash', minting_fee: '0.100000' }
  • ROLLBACK confirms no state leaked into the branch.

Test plan

  • npm run check — 0 new worker errors
  • Full worker integration suite green (94/94, was 64/64)
  • npx wrangler deploy --dry-run --env production succeeds
  • Live Neon state-change SQL (freeze→mint→trust update) validated via Neon MCP transaction with ROLLBACK
  • After merge: wrangler secret put OPENAI_API_KEY --env production
  • After merge: smoke-test curl assets.chitty.cc/api/v1/status confirms migration_status: "PHASE_3C_HEAVY_WRITES" and all 27 migrated routes listed
  • After merge: smoke-test POST /api/assets/:id/freeze once mint endpoint shape is verified live

🤖 Generated with Claude Code

…ate/trust/ledger/seed)

Ports 8 write routes with external service calls (ChittyMint, ChittyLedger,
OpenAI gpt-4o) and the dev-only seed-demo endpoint:

  POST /api/assets/:id/freeze                    server/routes.ts:125
  POST /api/assets/:id/mint                      server/routes.ts:166
  POST /api/evidence/:evidenceId/analyze         server/routes.ts:379
  POST /api/legal/generate-document              server/routes.ts:557
  POST /api/assets/:assetId/calculate-trust-score  server/routes.ts:596
  POST /api/evidence-ledger/submit               server/routes.ts:51
  POST /api/evidence-ledger/:chittyId/verify     server/routes.ts:92
  POST /api/seed-demo                            server/routes.ts:39 (dev-only)

External clients (3s per-call timeout, no retries; mirror Phase 2c pattern):
- worker/src/clients/openai.ts          (new — chat completions via fetch)
- worker/src/clients/chittymint.ts      (new — freeze + mint)
- worker/src/clients/chittyledger.ts    (extended — submit + verify)

Notes:
- OPENAI_API_KEY added to Env as optional Worker secret; handlers return
  503 service_unavailable when unset (no crash).
- /mint gates on chittyChainStatus === 'frozen' (matches Express; the
  "7-day elapsed" check is aspirational and not present in Express today).
- trust_score column is numeric(3,1) — OpenAI output clamped to [0, 99.9].
- /api/seed-demo returns 403 unless ENVIRONMENT === "development".
- Phase 3a chitty_id=NULL deferral is NOT resolved by this PR. /mint mints
  an evidence TOKEN, not the ChittyID. ChittyID generation at create-time
  remains a separate phase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 11f1b06e-ef46-40e4-aeed-8ce555e5a4a0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/hono-phase-3c-heavy-writes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

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

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: a06d1fa8de

ℹ️ 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 on lines +485 to +486
chittyChainStatus: "frozen",
ipfsHash: freezeResult.ipfsHash ?? null,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject freeze requests for non-draft assets

Add a state guard before mutating the asset here; this handler unconditionally sets chittyChainStatus to "frozen", so callers can refreeze assets that are already minted/settled/disputed. That breaks the documented lifecycle ordering and allows invalid backward transitions (and potentially repeated mint cycles) instead of enforcing draft -> frozen only.

Useful? React with 👍 / 👎.

},
],
});
const parsed = JSON.parse(content || '{"trustScore":0,"factors":[]}');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle malformed trust-score JSON without 500s

Wrap this parse in the same fallback behavior as the legacy service (server/aiAnalysis.ts), which returns a score of 0 on parse/model-shape failures. As written, a non-JSON or malformed OpenAI response throws SyntaxError, and /api/assets/:assetId/calculate-trust-score only maps OpenAIConfigError/OpenAIClientError, so this path becomes an internal 500 instead of a graceful trust-score fallback.

Useful? React with 👍 / 👎.

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