Developing the WordPress plugin + Drupal module
The Cinatra WordPress plugin and Drupal module live in their own repositories (the source of truth), not in this monorepo:
cinatra-ai/wordpress-plugin→ WordPress.org slugcinatra(GPL-2.0-or-later)cinatra-ai/drupal-module→ Drupal.org machine namecinatra(GPL-2.0-or-later)
For local development the dev docker stack consumes them as clones inside
this tree. cinatra setup {dev,branch,clone} clones / fast-forwards them from
the config in package.json (cinatra.devApps) into:
dev/wordpress-plugin/— the WordPress plugin (repo root maps to the plugin dir; docker bind-mounts it to/wp-content/plugins/cinatra/)dev/drupal-module/cinatra/— the Drupal module (docker bind-mounts it to/modules/custom/cinatra/)
Both clone paths are gitignored in the cinatra repo (they sit under dev/
alongside the other dev-only clones; nothing is tracked). See LINKING.md for the Apache-2.0 ↔
GPL-2.0-or-later HTTP-only boundary.
Where does my commit go?
Section titled “Where does my commit go?”| You changed… | Commit in… | PR in… |
|---|---|---|
| Plugin/module PHP, YAML, readme | the clone (dev/wordpress-plugin/ or dev/drupal-module/cinatra/) | the extracted repo — no cinatra PR needed |
| Cinatra core (bundle route, contracts, setup, docs) | the cinatra repo | the cinatra repo |
| The wire contract (both sides) | both — see “Contract-version bumps” | two coordinated PRs |
git status makes it obvious which tree you’re in: run it inside
dev/wordpress-plugin/ (or dev/drupal-module/cinatra/) and you’ll see the extracted
repo’s status (its own origin, its own main). Run it at the cinatra root
and the clone paths don’t appear at all (they’re gitignored).
Pure plugin/module change
Section titled “Pure plugin/module change”cd dev/wordpress-plugin(ordev/drupal-module/cinatra).- Edit, commit, push to the extracted repo’s
mainvia a PR there. - Back in cinatra,
cinatra setup devfast-forwards the local clone to the newmain. No cinatra PR is involved.
Contract-version bumps (cross-cutting changes)
Section titled “Contract-version bumps (cross-cutting changes)”When a change touches the wire contract (contracts/wp-drupal-assistant/), use
the two-PR protocol so a plugin built against the old contract never silently
breaks:
- cinatra first: add
contracts/wp-drupal-assistant/v2/(new schemas + fixtures), extendSUPPORTED_CONTRACT_VERSIONSinsrc/lib/wp-drupal-contract.ts(keepv1), ship the v2-aware backend. Merge it. - plugin/module next: bump the client’s
contractVersiontov2in the extracted repo, ship it. - Keep
v1supported until a deliberate deprecation PR removes it.
The contract test suite (tests/contracts/wp-drupal/) + the
wp-drupal-contract.yml gate enforce schema/fixture conformance on every change
under contracts/wp-drupal-assistant/.
Dirty-tree recipes
Section titled “Dirty-tree recipes”cinatra setup never destroys local work. If a clone has uncommitted changes it
is skipped with a warning naming the dirty repo. To proceed:
- Keep your work:
cd dev/wordpress-plugin && git commit -am "wip"(orgit stash), then re-runcinatra setup dev. - Discard your work:
cd dev/wordpress-plugin && git checkout . && git clean -fd, then re-run. - Force fast-forward (stashes first):
cinatra setup dev --force-dev-apps— stashes local changes, then hard-resets the clone toorigin/main. A clone pointing at the wrong origin or branch is never auto-reset, even with--force; fix the remote or move the dir aside. - Skip the sync entirely:
cinatra setup dev --skip-dev-apps(e.g. on CI / contributors without access to the private repos pre-launch).
Per-repo URL overrides (for a fork or an SSH remote) without editing
package.json: CINATRA_WORDPRESS_PLUGIN_REPO_URL,
CINATRA_DRUPAL_MODULE_REPO_URL (HTTPS or SSH; origin comparison normalizes the
two forms).
Temporary debugging
Section titled “Temporary debugging”error_log() (WordPress) and \Drupal::logger('cinatra')->debug() (Drupal)
changes you add while debugging live in the clone and are local-only unless
you intentionally commit + push them to the extracted repo. cinatra setup
treats them as a dirty tree and skips (see above) — so they won’t be silently
fast-forwarded away, but they also won’t reach anyone else until you push.
Recovery when cinatra setup skips a dirty clone
Section titled “Recovery when cinatra setup skips a dirty clone”The warning names the dirty repo and the exact next command. Pick one of the
dirty-tree recipes above. If a clone is in a non-git or wrong-origin
state, cinatra setup fails loud (non-zero exit) with remediation text rather
than touching it — move the directory aside and re-run to get a fresh clone.
Volume-scoped dev migration (post-cutover, one-time)
Section titled “Volume-scoped dev migration (post-cutover, one-time)”Existing dev installs have the pre-rename (widget-era) plugin/module baked into
the WordPress/Drupal docker volumes. To pick up the renamed cinatra plugin/module,
drop only the WP/Drupal volumes (not unrelated cinatra volumes) and re-setup:
docker compose stop wordpress wordpress-db drupal drupal-dbdocker volume rm cinatra_cinatra-wordpress cinatra_cinatra-wordpress-db \ cinatra_cinatra-drupal cinatra_cinatra-drupal-dbcinatra setup devThe renamed plugin/module each run a one-shot migration that copies the
pre-rename WordPress option keys / Drupal settings config into the new
cinatra_* / cinatra.settings keys (the exact legacy identifiers live in the
extracted repos’ migration code), so a real (non-volume-dropped) upgrade
preserves the configured URL + API key. Browser-side localStorage chat history
resets (the history key was renamed).
UAT gate + override label
Section titled “UAT gate + override label”PRs that touch the contract / plugin-integration paths trigger the Playwright
UAT hard gate (wp-drupal-uat.yml). To land a planned migration that knowingly
breaks a UAT, add the override-wp-drupal-uat label — see the override process
in contributing.md. Running the UATs locally:
pnpm test:e2e:wp-drupal (see tests/e2e/wp-drupal-uat/README.md).