# Plantoo Tech — Codex project guide

This is the Codex entry point for the repository. The inherited `claude.md` remains the detailed
architecture and style reference; do not delete or rewrite it merely to rename the assistant.

## Before changing code

- Work locally only. Never read `.env`, credential directories, SSH keys, tokens, API keys, or
  passwords. Never SSH, deploy, restart a remote service, or run a command against a remote or
  production database. Give Arfu the exact remote command to run when one is needed.
- Never run `git add`, `git commit`, or `git push`. Arfu owns commits and deployment.
- Check `git status` first and preserve all existing changes. Do not edit or discard unrelated
  work.
- Read the relevant contract before implementing:
  - `docs/flow-schema.md` for flow nodes and runtime behavior.
  - `docs/progress.md` for dated product decisions and the newest build record.
  - `docs/coding-style.md` for the full house style.
  - `API.md`, `DEPLOY.md`, and `TESTING.md` for their respective surfaces.

## Which source wins

The repository contains historical handoff material. Resolve conflicts in this order:

1. Current code, migrations, and tests.
2. `docs/flow-schema.md` for the flow contract.
3. The newest dated entry in `docs/progress.md`.
4. Durable architecture and behavior rules in `claude.md`.
5. Root summaries such as `README.md`, `TESTING.md`, and `HANDOFF.md`, whose counts and status can
   lag behind the code.

`HANDOFF.md` records a completed September handoff and is not a current task list. The MVP plan is
closed. `docs/fp-test-plan.html` is the current full-product manual plan and is generated from
`docs/progress.md` by `docs/fp-test-plan.gen.py`; do not hand-edit its generated card data.

## Product and architecture

- Plantoo is a multi-tenant chatflow, matching, document, payment, and booking platform.
- It has three host-scoped React panels: platform admin, organization staff, and end user.
- Web registration is invitation-only. Do not add open signup or restore the removed anonymous
  contact/widget `/api/v1/*` surface.
- `app/Flow/*` is a deterministic reducer. Keep it free of database, HTTP, queue, clock, and tenant
  lookups. `FlowRunner` owns runtime effects.
- `docs/flow-schema.md` is the source of truth. Never invent a flow node or field in code first;
  update the contract and version it as part of the same change.
- MySQL owns relational state. MongoDB owns flow versions, chat/run/message documents, dynamic
  details, membership documents, and related document-shaped state. Mongo models use singular
  `protected $table` names. Mongo ObjectId references remain 24-character strings.
- A transaction covers one datastore only. Prefer one datastore per operation. When a second store
  is unavoidable, commit MySQL first and perform the document-side work afterward, preferably in
  an idempotent queued step.
- Every tenant-owned read and write must be organization-scoped. Cross-tenant access is always a
  defect; keep `tests/Feature/TenancyIsolationTest.php` green.
- All booking writes go through `App\Services\BookingService`.
- Use the atomic helpers on `App\Models\User` for the Mongo `user_org_belonging` document; never
  rewrite its organizations array directly.
- The Node worker in `worker/` handles document rendering, signing, and workbook calculation via
  the MySQL `render_job` contract.

## Durable product decisions

- A booking is the whole engagement; an appointment is one scheduled meeting inside it. A valid
  booking may never have an appointment. Keep their state machines separate.
- Two-sided matching uses two private chats and two runs of one flow definition. Link the runs;
  never merge the transcripts or expose one side's messages and documents to the other without an
  explicit relay/grant.
- An unbound flow is valid, especially for child-flow reuse. Do not warn that every flow must be
  attached to a service.
- Service status has three meanings: Enabled accepts new work; Closed accepts no new booking but
  existing participants may continue and add appointments; Archived is hidden and requires the
  relevant roster to be empty.
- Web chat is the record. WhatsApp and Messenger carry a copy on the one receive channel the member
  selected for that organization. Outside the messaging window, send only the organization's
  new-message notice with a one-use sign-in link; perform steps in-channel wherever supported.
- Each organization supplies and manages its own Meta app, credentials, numbers, Pages, templates,
  reviews, and charges through Settings. Never seed or handle a real tenant's credentials.
- Channel-created accounts are allowed, but web signup remains invitation-only. Do not infer a
  verified identity or merge accounts from a phone number merely typed into a profile.

## Owner workflow and acceptance

- Automated tests are evidence, not product acceptance. Until Arfu has exercised a feature, report
  it as built and automatically tested but unverified by hand.
- When behavior changes what `docs/progress.md` or the full-product test plan says, update the
  source material in the same task and regenerate `docs/fp-test-plan.html`. The plan contains only
  actions a person can perform in the UI; automated-only checks stay out of it.
- A changed node, node behavior, or test service also needs the WhatsApp and Facebook manual rows
  maintained in `docs/fp_test_plan_channels.py` where applicable.
- Tester-facing documents are user guides: omit commands, paths, class/seeder names, schema
  versions, and card ids. Use a white self-contained page, put UTF-8 metadata first, use complete
  links, and open local HTML only when Arfu asks.
- Testers use an organization of their own. Test Org is a developer fixture that seeders may reset.
  Never put fake users or demo fixtures into a real tenant; a real tenant's own agreed content may
  be seeded while the product remains in testing.
- The commented reference seeders at the top of `DatabaseSeeder` are a per-release toggle. Change
  or uncomment only the seeder whose reference data is meant to ship; do not treat the comments as
  permanent disablement or routinely enable every destructive seeder.

## Non-obvious traps

- Never run or recommend `php artisan storage:link`. `StorageController` deliberately owns stored
  file delivery so route caching cannot remove the route.
- A queued timer must refuse to fire before `fire_at`, and state must be saved before dispatching a
  job that reloads it. Queue delay is not a correctness guarantee.
- Mongo `_id` values are ObjectIds even when application code exposes 24-character strings.
  `whereIn("_id", $stringIds)->delete()` can report success while deleting nothing; delete fetched
  models and verify what remains.
- Do not edit sibling tenant repositories such as `../daydayhelp` or `../anoath`. They may be read
  for reference only; Plantoo work stops at our implementation plus a clear integration request.

## Implementation conventions

- When editing an existing file, match its local style. Do not half-convert legacy files.
- New identifiers are `camelCase`; `snake_case` is only for database columns, payload keys, and
  other data boundaries. Classes/components use `PascalCase`.
- Use four spaces, K&R braces, double quotes, and short `//` comments. Do not add docblocks. A
  comment should explain a decision or trap, not narrate the code.
- Keep new controllers thin, put business rules in services, and use Form Requests for validation.
- Use Eloquent in application code, not raw SQL. Scope SQL transactions to the named `mysql`
  connection, catch `\Throwable`, roll back, log, and rethrow. Never exit a transaction before a
  commit or rollback.
- Persisted states and options use int-backed enums in `app/Enums/`, one per file, stored in
  `unsignedTinyInteger` columns and mirrored in `resources/js/shared/status.js` when the frontend
  needs them. `CommonOption` is the URL-slug exception.
- Routes use verb-suffix RPC paths and controller actions. Do not add route closures because route
  caching must keep working.
- Migrations are reversible. Never edit a pushed migration. Do not combine destructive contract
  changes with the deploy that introduces their replacement; use expand, migrate, then contract.
- Frontend code is plain JavaScript with React function components and hooks. Import PrimeReact
  through `@/prime-react`. Use shared toast/dialog/modal primitives, never native
  `alert`/`confirm`/`prompt`.
- Tests are class-based PHPUnit, never Pest. For a bug, first add a regression test that fails on
  the old behavior.

## Verification

Use focused tests while iterating. Before declaring an implementation complete, run:

```bash
composer test
npx vite build --mode development
npm run test:worker
```

Run Playwright when the changed behavior crosses a browser flow and the required local services are
available. Do not infer current suite counts from old documentation; report the command's actual
result.

## Current phase

The MVP is complete and the repository is in full-product development. Treat new requests as new
work, not as unfinished items from `HANDOFF.md`. Do not invent a roadmap, select a backlog card, or
sequence work unless Arfu asks. As of 2026-10-09 the flow contract is v0.42; recent work centers on
WhatsApp/Messenger delivery, rich message templates, and helper-pool/one-case matching.
