Status: Active Last updated: 2026-01-29
This file is the operating contract for humans and agents working in this repo. It is authoritative after CI/workflows and guardrail registries. If something conflicts, fix the lower authority, not the higher. This guide defers to docs/doctrine.md for all execution invariants and enforcement rules.
package.jsonscripts +.github/workflows/*(actual behavior)- Guardrail registries and tests (
scripts/guardrails/*,tests/guardrails/*) AGENTS.md(this file)docs/*(specs and explanations)- Everything else (notes, drafts, marketing)
- Cherry is a real-time spending copilot, not a card, proxy, processor, or payment terminal.
- Cherry never fronts transactions, holds funds, or touches payment rails.
- Cherry Vine is a context beacon (merchant + amount + timestamp), not a reader or terminal.
- Cherry Pass is a storeCard-style advisory pass; it is not a payment instrument.
- Recommendation sessions and Cherry Points are advisory and sandboxed.
Forbidden framings: “fronting card,” “proxy BIN,” “tap to pay with Cherry,” “Cherry terminal,” “payment card.”
- Core loop: Observe → Evaluate → Recommend → Reward.
/api/scanruns the engine and logs aDecisionEventfor telemetry; it does not create sessions or ledger rows.- Sessions + ledger persistence happen via
/api/sessionsand confirm/verify flows. - Vine ingest (
/api/vine/order) is dev-only and context-only; no payment rails. - Wallet pass (
/api/wallet/cherry-pass) returns 501 unless fully configured and explicitly enabled. - Engine is deterministic and pure: it consumes
EngineState+EngineContextand emits ranked decisions.
- Do not call
Date.now()ornew Date()inlib/engine/*orlib/authority/*. - Time enters at boundaries (API routes, adapters) and is passed in as
nowMs. - Guardrails enforce deterministic core behavior.
- Server components must not call
Date.now(),Math.random(), or locale-dependent formatting. - Pages must render from a single data snapshot; no structural changes on hydration.
- Empty/data states must share a stable outer container.
- Prisma client must only be instantiated in
lib/prisma.tsand consumed by runtime adapters. - Engine and authority logic must not import Prisma or
@prisma/client. - Zod schemas live in
lib/schemas/*; parse viaparseJsonBodyfromlib/validation.ts. - Stateful API routes must use
withUserand return401for unauthenticated requests. - Bank ingest invariants:
BankTransaction.idis internal and never set from ingest data.- Idempotency key is
(userId, externalId)only. - All ingest writes go through
upsertBankTransactions.
- Bucket math must flow through
lib/buckets-runtime.ts. - Authoritative fields:
budgetAmount,spentCents. - Derived only:
committedCents,remainingCents. currentAmountis legacy-mirror only; never authoritative.
- Evaluator code is read-only with respect to Sessions, Ledger, Buckets.
- Outputs are diagnostic only and must never affect user-facing decisions.
- Must call
assertOfflineEvaluatorModelsReady()before DB access. - Gate execution behind
CHERRY_OFFLINE_EVALUATOR_ENABLED. - Never hard-code runIds; derive via
defaultRunIdForUser(userId, now).
- Guardrails are registered in
scripts/guardrails/registry.mtsand must run vianpm run check. - Guardrails are unaddressable by path; run them only via
npm run check. - Guardrails must be deterministic and side-effect free; no network or DB I/O.
- Do not weaken guardrail severity or bypass guardrail tests.
- DB truth scripts and DB tests may assert only: existence, impossibility, conservation.
- Allowed: constraint exists, duplicate key fails, FK missing parent fails, NOT NULL fails, count stays 1.
- Forbidden: preferred outcomes, query plans, performance targets, business logic behaviors, ordering.
- Rule of thumb: if the DB could allow multiple valid outcomes, do not assert a preference in DB truth.
- Repo is ESM by extension.
.mtsis allowed only underscripts/. .mtsscripts must be run vianpm run ts:esm -- <script>..tsfiles underscripts/must not use ESM syntax.- Script imports must use runtime extensions (
.js/.mjs/.cjs); no@/aliases.
.github/workflows/ci.ymlrunsnpm cithennpm run ci:verify.- Tests run with Prisma mocked; CI green does not fully prove DB behavior.
.github/workflows/env-checks.ymlprovisions Postgres and runscheck:envplustest:db.
- Keep API handlers thin; move domain logic into
lib/. - Use
safeSolveDecisionForWorld/safeSolveDecisionForUserfor engine decisions. - For schema changes:
npx prisma formatnpx prisma migrate dev --name <desc>npx prisma generate- Run
npm run check,npm test, andnpm run build.
- For docs: add
Status+Last updated, split Current vs Future, add Related docs.
From now on, the agent must not treat direct commits to main as the normal workflow.
The default development flow is:
- Start from updated
main. - Create a focused branch.
- Implement the requested change on that branch.
- Run the relevant verification commands.
- Commit the change with a clear conventional commit message.
- Push the branch.
- Open or prepare a pull request into
main. - Do not merge unless explicitly instructed.
Canonical command flow:
git checkout main
git pull --ff-only
git checkout -b <type>/<short-description>
# implement changes
git status --short
npm run check
npm test
npm run build
git add .
git commit -m "<type>: <summary>"
git push -u origin <type>/<short-description>The pull request must include:
## Summary
- what changed
## Testing
- commands run
- results
## Risk
- changed domains
- possible regressionsHard rules:
- Never commit directly to
mainunless the user explicitly says to. - Never merge a PR unless the user explicitly says to.
- Never skip verification silently.
- If verification fails, stop and report the failure.
- If the working tree is dirty before starting, inspect it first and do not overwrite unrelated user changes.
- If a branch already exists, reuse it only if it matches the requested task; otherwise create a new branch.
- Each PR should represent one coherent change. Do not bundle unrelated fixes.
- Prefer
squash and mergewhen the user later asks to merge.
This is now the default operating model for all future repo work.
npm run check→ guardrails + lint + typecheck are green.npm test→ unit/guardrail tests green (Prisma mocked by loader).npm run build→ Next.js build passes.npm run ci:verify→ mirrors CI entrypoint.- If schema changed: migrations apply and Prisma client is regenerated.
- If docs conflict with code, update docs to match reality unless legal constraints require a code fix.
- If code conflicts with legal constraints, fix code and add guardrail/tests rather than weakening docs.
- Do not update guardrail fixtures unless you also update the corresponding tests intentionally.
Cherry is product-ready for a pilot when:
- Engine decisions are deterministic and stable across core surfaces.
- Sessions + ledger lifecycle is reliable (no double-award, clear pending/posted rules).
- Vine and Wallet remain advisory-only and correctly gated.
- Observability (DecisionEvent logging, guardrails) is in place and CI is green.
- Full bank ingest verification and automated ledger posting beyond current stubs.
- Enforced Vine signature lifecycle and expanded device coverage.
- Marketing and user-facing surfaces under
app/(marketing)and richer user shell routes.
docs/legal-constraints.mddocs/cherry-vision.mddocs/ci-and-guardrails.mddocs/guardrails.mddocs/script-standards.mddocs/repo-structure.md