agentjob.ioHandbook

Handoff

Where the product stands, what was decided and why, what is verified, and what is left. Read this before resuming work on Wateera.

Handoff — Wateera & Wajhati

Last updated: 2026-09-25 · State: product-complete, payments integrated (Moyasar + Tamara BNPL), Wajhati two-sided hiring platform established with cryptographic bundle verification and SEO public directory.

Resuming work? Read this file, then AGENTS.md. This is a living document: update it at the end of any session that changes the state below.

1. Where everything is

Product repositoryagentjob.io monorepo
Working copy/Users/fares-wdeftksa/development/agentjob.io
Core packages@repo/payments (Moyasar + Tamara BNPL), @repo/core (Wajhati domain, hashing, matching), @repo/database (Prisma schema + queries), @repo/api (oRPC modules), @repo/i18n (bilingual ar/en)
SaaS applicationapps/saas (Daily performance + Wajhati candidate, discovery, finite batches, employer portal)
Public marketingapps/marketing (Landing pages + /jobs SEO directory targeting Arabic head term وظائف)
Local databasePostgreSQL on localhost:5432, database app; demo workspace demo-team

Environment rule learned the hard way: node-linker layout, ports and URLs must agree. SAAS_PORT/MARKETING_PORT/DOCS_PORT (3100/3101/3102) drive the dev scripts and must match the NEXT_PUBLIC_* URLs in .env.local.

2. What the product is

  1. وتيرة (Wateera): Daily-performance loop (plan, execute, score, coach agents).
  2. وجهتي (Wajhati): An independent two-sided Saudi talent and hiring platform (agentjob.io):
    • Candidate side: Default-private verifiable vaults, provenance-backed facts, finite approved application batches with cryptographic SHA-256 bundle hashes (computeBundleHash, computeBatchManifestHash).
    • Employer side: Vacancy publication, consented candidate reviews, interview scheduling.
    • Public SEO Directory: /jobs targeting the Arabic search term "وظائف" with JSON-LD JobPosting schema.
    • Payments: Moyasar (Mada, Credit Card, Apple Pay) + Tamara BNPL (3/4/6 installments) via @repo/payments.
SurfaceRoute
The day (plan, tasks, timer, close, plan agent)/<workspace>/today
Wajhati Candidate Profile & Matching/<workspace>/wajhati
Wajhati Company Discovery Agent/<workspace>/wajhati/discovery
Wajhati Finite Application Batches/<workspace>/wajhati/batches
Wajhati Employer Hiring Portal/<workspace>/wajhati/employer
Public Jobs Directory (SEO وظائف)/jobs (bilingual, city filters, JSON-LD)
Payments checkout & webhooksMoyasar + Tamara BNPL via @repo/payments
Assistant accessMCP server, packages/mcp — see docs/mcp.md

3. Decisions already made (do not re-litigate without new evidence)

DecisionWhy
Tamara BNPL & Moyasar integrationMarket-standard Saudi payment rails: Mada, Credit Cards, Apple Pay via Moyasar, and Pay in installments (3/4/6) via Tamara. Fully covered by unit tests (13 tests each).
Zero-Haiku Strict Guardrail (D05)Claude Haiku is strictly barred from all model execution, tool calls, and fallbacks. Enforced by assertNoHaiku and tested in CI.
Complete Jadarat Exclusion (D03)Strictly 0 connectors, crawlers, placeholders, or dependencies on Jadarat. Enforced by assertNoJadarat and verified in unit tests.
Deterministic bundle & manifest hashingEvery application bundle is keyed by a canonical JSON SHA-256 hash (computeBundleHash). Batches cannot be altered after approval.
Truthful matching engineMatches only confirmed candidate facts against vacancy requirements; gaps are computed deterministically without hallucinations.
Deterministic engine first, model optionalWorks with no API key, testable in CI, no running cost; the model is a second implementation of the same schema, with same-request fallback. Every run stores which engine answered.
Plan gating in the API, not the UIOne resolver (resolveTenant / resolveWajhatiAccess), capability maps, refusals audited.
One output shape for every patternOne panel renders all of them, and stored runs stay readable across releases.
Measurement is domain code with a published formula70% impact-weighted completion + 20% focus minutes + 10% closing the day; streaks count closed days only. A score nobody can derive is a score nobody trusts.
Plan gating in the API, not the UIOne resolver (resolveTenant), one capability map, refusals audited.
Display copy only in i18n; catalogs hold idsTwo locales stay in sync; generated content is authored in the request locale by the engine.
One product module, vertical removedmodules/registry loads performance only; the template's Saudi merchant surfaces were deleted (ADR-0010), and the module contract was trimmed to what a loaded module uses.
No containerspnpm setup / db:setup / db:seed against a local Postgres; docker-compose.yml deleted.
Cloudflare via OpenNext, not vinextnpx vinext check scores this app 88% with two issues, one load-bearing: Vite ignores the webpack config that bundles Prisma and shims pg-native/cloudflare:sockets, and next/font/google stops being self-hosted. OpenNext consumes next build's output, so the verified bundle is the deployed bundle. Revisit once Prisma bundling has a Vite-native answer.
pg-cloudflare patchedIts default export condition points at a stub while its workerd condition points at the real socket; the adapter's tracer and bundler disagreed. patches/[email protected] makes both resolve the real file. Remove when the adapter's tracer becomes condition-aware — the failure to watch for is the same Could not resolve "pg-cloudflare".
Workspace API keys act as their creator, inside one workspacewk_<prefix>_<secret> in hex (base64url's _ makes the separator ambiguous), SHA-256 at rest, plaintext shown once. Authenticated in protectedProcedure, so every existing procedure is key-accessible with no rewrite, and a cross-workspace call is refused and audited.

4. Verified state (as of the last session)

pnpm format:check && pnpm lint && pnpm type-check && pnpm test   # gates
pnpm build                                                       # 4 apps
pnpm build:worker && pnpm --filter saas exec wrangler deploy --dry-run
cp .agents/skills/template-to-product/scripts/verify-product.mjs apps/saas/ && \
  DEMO_EMAIL=... DEMO_PASSWORD=... node apps/saas/verify-product.mjs   # browser pass
  • lint: 0 errors · type-check: 25 tasks passing · tests: 13 suites / 247 tests passing (including 26 payments tests, 60 core tests, 57 api tests) · build: 4/4 apps.
  • Payments: Moyasar and Tamara BNPL providers fully tested with unit tests covering checkout links, webhooks, and idempotent records.
  • Wajhati platform: candidate vaults, finite application batch cryptographic hashing, truthful matching, discovery agent, employer portal, and SEO public jobs directory verified.
  • Browser pass on routes: no console errors.
  • MCP: an end-to-end stdio run listed seven tools and called get_today, add_tasks, get_usage, run_pattern; an invalid task returned readable text; a cross-workspace call returned 403 and an audit entry.

5. What is left, and who owns it

Blocking a paid launch

ItemOwnerNotes
Deploy to a live Worker + production Postgres + domainneeds owner's Cloudflare account (wrangler login, account id, secrets)code path is ready
Moyasar live keys + webhook registered + one real subscriptionownerdocs/deploy.md §payments
Mail domain verified (SPF/DKIM/DMARC)owner's DNSotherwise magic links and invitations fail
SMS sender id registeredowner's Unifonic/Msegat accountphone OTP is the default login mode
Legal review of privacy policy and terms, [ ] placeholders filleda lawyerdrafts are written, not reviewed
Terms acceptance at signupagentmissing today
Data retention/deletion matching the policy (30-day content, 12-month audit, 24-month usage)agentaccount deletion exists; the purge does not
Monitoring on /api/health?deep=1, 5xx alerting, a restored backup, incident contactagent + ownernothing exists yet
MCP key management UIagentCLI only today (pnpm --filter @repo/scripts api-key)

Not blocking, known and documented: no scheduled jobs (no reminders), no notification types beyond two, MCP server runs from the repo rather than a published package, no pricing validation.

6. Traps that already cost time (do not rediscover them)

  1. Two working copies of one repo drift. It happened (development/tamp vs Glm/tamp), and a pushed commit arrived in one while a local fix waited in the other. Keep one copy, or one branch per copy.
  2. The dev server and pnpm build share apps/*/.next. Building while dev runs wedges the dev server, and the next browser pass times out on login with no useful error. Stop dev first.
  3. A reset database looks like a code bug. 401 User not found means re-seed (pnpm db:seed); the seed prints a new demo password each run.
  4. A running dev server holds a stale Prisma client and stale workspace packages. After a schema change or a change in packages/*, restart it (and clear .next if it still misbehaves).
  5. MISSING_MESSAGE is fatal to a page. Rewriting an i18n namespace silently drops keys other components still use (app.menu.* is the easy one to break).
  6. Plan copy lives in shared.json only. A pricing.* block in marketing.json shadows it and ships two prices for one plan.
  7. NFKD breaks Arabic filenames (أ → ا + a mark the "safe characters" filter then deletes). Use NFC and allow \p{M}.
  8. A warm hue holds more chroma than a cool one. Copy the shape of the accent ramp, never the numbers, when retinting the brand. --accent-500 is #E75724 exactly; --accent-600 is the solid-button step that keeps white labels ≥ 4.5:1.
  9. History rewrites need --force-with-lease. Done once to drop GitHub's first commit; the remote was otherwise untouched.
  10. GitHub reports a private repo you cannot see as "not found". The account switched mid-session (alswat1222 ↔ diamondcloudai) and a push failed with the same message a deleted repo gives. Check gh auth status before concluding anything was deleted.
  11. The disk was full (116 MB free) at one point, which silently blocks installs. Regenerable artifacts under ~/development were cleaned (38.3 GB) and 597 tracked files that were served build output (a Laravel public/admin/dist) were restored. Watch df -h; a node_modules tree per project plus a .next per app adds up.

7. Security notes

  • No secrets are stored in this document or the repo. .env.local is gitignored; only .env.local.example with empty values is tracked.
  • A demo API key for the demo-team workspace exists in the local database (prefix bea4ccdc). It points at localhost and is worthless elsewhere, but revoke it if in doubt: pnpm --filter @repo/scripts api-key -- --revoke <keyId> --workspace demo-team.
  • BETTER_AUTH_SECRET must be unique per environment; rotating it signs everyone out.

On this page