CPN Org Dev Workflow
Local cloud-pi-native console repo: structure, stack, contribution rules,
local dev, quality gates, issues/PR workflow. Does not provision clusters or
operate remote environments unless asked.
When to Use
"How do I contribute to Cloud Pi Native console?" / "What is the console backend
target and repo layout?" / "What commands for console local dev, lint, tests?" /
"What is the CPiN PR/commit/review workflow?" / "Implement/fix something in
server-nestjs (issue-first, module-consistent, tested)" / "Write a vitest or e2e
spec for a server-nestjs module".
Échelle de coordination (stack → async → swarm)
Choisis l'outil de coordination par nombre d'unités et infrastructure avant de
créer la branche ; l'échelle prend le minimum qui convient :
- Une unité →
cpn-stack — workspace jj frais épinglé sur main@origin,
bookmark limité à ce workspace. Défaut pour un correctif isolé ; obligatoire
si le checkout contient du WIP concurrent à ne pas mélanger.
- N unités parallèles, un dépôt →
cpn-async — un workspace par unité, DAG
depth/join via jj new <a> <b>, PR indépendantes ou en chaîne ; contrats
partagés figés avant fan-out.
- Unités exigeant capacités ou machines différentes →
cpn-swarm — routage
A2A par capacité, machine et pression runner ; jamais pour quelques PR sœurs
dans un dépôt (c'est cpn-async).
- Flux unique, checkout propre → aucun skill de coordination ; boucle
normale ci-dessous.
L'escalade est à sens unique : cpn-stack → cpn-async → cpn-swarm. Pas de
swarm pour une unité, pas de fan-out avant le ledger d'issue réglé.
Phases
Work-item lifecycle; gate phases are the mechanical walls a change must clear.
| # |
Phase |
Owner |
Gate |
| 0 |
Discussion (RFC) — only if the problem isn't converged |
cpn-discussion |
entry |
| 1 |
Issue — French problem statement + Définition du fini ledger |
cpn-issue |
ledger set |
| 2 |
Triage — labels/assignee/milestone/project/reviewers |
cpn-issue-triage / cpn-pr-triage |
ledger settled |
| 3 |
Branch + implement (jj workspace, conventional commits) |
this skill |
— |
| 4 |
Commit (conventional, SSH-signed) |
cpn-commit |
commit shape |
| 5 |
Code review (adversarial pre-merge) |
cpn-pr-review |
review gate |
| 6 |
PR (origin-only draft, link Issues liées) |
cpn-pr |
— |
| 7 |
Land (gh pr merge --squash + merge queue) |
this skill |
branch protection |
| 8 |
Close deliberately (verify N of N) |
cpn-issue |
ledger discharged |
Phases 2 and 5 are the before-code / before-merge gates. The console Procedure
(steps 1–12) implements phases 3–7.
Prerequisites
- Local checkout at
~/Source/Repos/github.com/cloud-pi-native
- Docker >= 27 (compose >= 2.35, buildx), Node.js >= 24, pnpm >= 10
How to Run
Use read_file / search_files on the repo paths; run console commands via
terminal from the console dir.
Parallelize independent, non-overlapping module changes with delegate_task
(e.g. implementation + its vitest spec, or two unrelated modules) as
self-contained subagents, each given this skill's context and the target repo
path. Keep dependent steps (typecheck/test after code) in the same task. Fix
shared contracts (interfaces, exported types, file ownership) before fan-out. A
subagent's "done" is self-certification: after merging, the dispatcher re-runs
each leaf's checks itself via terminal.
delegate_task(tasks=[
{"goal": "Implement <module> in <org>/<repo>: <contract>. Run <lint/test> "
"and confirm green before reporting done. Keep dependent "
"typecheck/test in the same task.",
"context": "cpn console repo; one workspace per unit per cpn-async; "
"contracts fixed before fan-out.",
"toolsets": ["terminal", "file"]},
])
Quick Reference
console/README.md — overview, architecture, ports, run modes
console/CONTRIBUTING.md — scope, backend target, quality gates
console/package.json — workspace scripts: lint, test, build, docker
console/.github/PULL_REQUEST_TEMPLATE.md — required PR sections
console/apps/server-nestjs — current backend target; console/apps/server —
historical, do not modify
console/misc/plugins.md — plugin lifecycle; console/playwright/README.md —
Playwright e2e
Procedure
Read console/CONTRIBUTING.md before changing backend/dependency behavior.
Backend target is apps/server-nestjs; never touch apps/server.
Issue-first is mandatory. Lifecycle: discussion → issue → issue comments
→ PR. Unclear problem → open a Discussion RFC first (cpn-discussion). Issue
body = problem statement (need, scope, API/data/security impact, - [ ]
acceptance tasklist — see Gates), never the solution; analysis goes in
comments. One issue per item. Create via cpn-issue if absent; link PR with
Refs #N (fermer délibérément après N-sur-N, voir references/pitfalls.md).
No bare-request implementation; no PR without an issue behind it.
Reference-safety: #N in a PR/commit body resolves to console issue/PR N
and Closes / Fixes / Resolves auto-close it on merge. Bare #N is only
safe for a console ticket; cross-repo refs use a full URL or owner/repo#N.
Triage before work (cpn-issue-triage): assign every exposed metadata —
labels (conventional-prefix → type), assignee (active gh identity),
milestone (bug → current patch, feature → next release), project if obvious,
reviewers. Apply only empty, determinable fields; never invent a label.
From console: pnpm install, pnpm build,
pnpm --filter @cpn-console/server-nestjs run db:generate.
Launch: local+remote → pnpm run dev then
pnpm --filter @cpn-console/server-nestjs run dev +
pnpm --filter @cpn-console/client run dev; full containerized →
pnpm run docker:dev; integration → pnpm run docker:integ or
pnpm run integ.
Checks before submit: pnpm lint, pnpm test, pnpm playwright:test if a
journey is affected.
Fresh jj workspace per item (une unité → cpn-stack, voir Échelle de
coordination) : jj workspace add -m <repo>.<name> . ../<repo>.<name> (or
jj workspace add --revision <base> <path>). Build a stack of small child
commits; branch out with jj new <other-parent> when a commit doesn't need
its parent. Multiple children of one parent → jj diamond (natively
tracked, parallelizes landing). Fully independent streams → own
jj workspace add ../<repo>.<name> --name <repo>.<name> at @ and own a
standalone PR (cpn-async: fan-out, join jj new <a> <b>, land via
standalone gh pr).
Don't stack unless a later module imports an earlier one's new code.
Conventional English commits, one per unit. jj-backed — never
git commit; use jj describe -m "msg" / jj new -m "msg". Fold into
existing: jj log -r '::@'; if covered,
jj squash -m "msg" -f @ -t <existing> instead of a new commit. Detect jj
via jj status / .jj/; git reflog showing export from jj means
jj-owned.
Before PR: (a) conflict-check children — jj log -r '::@', rebase each onto
parent (jj rebase -d <parent> -r <child>); 2-sided <<<<<<< resolves in
WC (jj status clears). A <name> (conflicted) bookmark is a remote
tracking conflict — surface it, don't jj bookmark set. (b) Re-describe:
jj log -r '@-|@'; if a squash merged concerns,
jj describe -r <rev> -m "msg".
Open DRAFT PR via .github/PULL_REQUEST_TEMPLATE.md:
gh pr create --draft --fill --body "Refs #N"
Don't mark ready until review passes; WIP at handoff → leave draft + say so
(migration PRs draft unless told). 12. Code review before merge
(cpn-pr-review): adversarial over diff — architecture, conventional-commit +
French rules, trust-boundary security, root-cause vs symptom. The review is the
gate; don't mark ready until findings resolved or explicitly waived.
Validate assumptions before work
Probe each requirement and RECORD the result; an unmet requirement is a reported
blocker, never a silent scope change:
- gh identity + write:
gh api user --jq .login and
gh api repos/cloud-pi-native/console --jq .viewerPermission — need
write/admin to push to origin.
- Toolchain:
node --version (≥24), pnpm --version; jj present (console is
jj-backed — never git commit; use jj describe/jj new).
- The issue exists (issue-first; create via
cpn-issue if not).
Report shape: BLOCKED: <requirement> — <evidence> — <recovery path>. Unblocked
streams may fan out (cpn-async); the blocked stream is surfaced.
Landing and PR↔commit parity
Landing follows the same origin-only discipline as the other cpn skills:
- Origin-only. Clone the org repo so
origin is cloud-pi-native/*, push
the branch to origin, and open the PR with
--head cloud-pi-native:<branch>. (Pre-2026-08 --head shikanime:<branch>
guidance is retired.)
- Plain
gh pr is the landing path (the org removed the gh stack
extension). Before opening each PR run the cpn-pr duplicate/stack check
(step 1b): no new PR if an open one already delivers the change; stack on the
existing PR's branch when your change depends on it. Open each branch with
gh pr create --draft --fill --body "Refs #N" and land with
gh pr merge --squash; parity is enforced by review, not tooling.
- Une PR résout toujours une issue ; ne jamais l'ouvrir seule. La liaison
est many-to-many : plusieurs PR peuvent résoudre une issue ; une PR peut
en servir plusieurs. Par défaut
Refs #N sur chaque PR. Tout autre cas :
après la fusion de la PR finale, vérifier la tâche N sur N et fermer
délibérément (gh issue close <N> -c "<evidence>").
- Parity principle: the commit is the source of truth; the PR restates it.
The PR title must equal the commit subject and the PR body must restate the
commit message — don't add new rationale the commit doesn't state (see
cpn-commit / cpn-pr). Author the commit to carry the full rationale
(subject + blank line + body) so the PR restates it without inventing claims.
Deep detail (gates, module design, vitest/e2e/Playwright rules)
These are kept out of the always-loaded footprint; load on demand:
- Gates (done-is-proven theory, tasklist-in-issue, wall, report audit):
references/dev-detail.md
- Implementation consistency (module design, Prisma via
-queries.utils.ts,
config injection, hooks flags): references/dev-detail.md
- Testing practice (vitest spec rules, mockDeep, faker, no describe-scope
calls) and E2E / Playwright requirements:
references/dev-detail.md
Migration PR review (Fastify → server-nestjs)
A migration PR can pass typecheck/lint/unit yet still ship a silent sync
regression: the new service emits domain events via EventEmitter2 but
nothing bridges them into the plugin hook system, so Keycloak/GitLab group syncs
stop at cutover. Before approving, run the parity checklist in
references/migration-parity-checklist.md (grep emitted events vs @OnEvent
consumers vs legacy hook.<entity> calls). Core rule: every
eventEmitter.emitAsync('<entity>.<verb>') must have a corresponding
@OnEvent('<entity>.<verb>') → capturePluginResult handler, or the cutover is
blocked.
Pitfalls
Optional edge cases and gotchas — load references/pitfalls.md on demand.
Verification
Run read_file on console/CONTRIBUTING.md and confirm it states
apps/server-nestjs as the backend target and lists pnpm lint, pnpm test,
and pnpm playwright:test as pre-submission checks.
See also
cpn-commit — the commit shape (conventional subject, author identity, SSH
signing) this workflow lands.
cpn-pr — origin-only PR opening from these commits.
cpn-stack — isolation d'une unité en workspace frais.
cpn-swarm — cluster d'agents A2A.
1---2name: cpn-dev-workflow3description: À utiliser quand vous travaillez dans le dépôt console cloud-pi-native/console : règles de contribution, cycle de dev et workflow PR.4license: Apache-2.05---67# CPN Org Dev Workflow89Local `cloud-pi-native` console repo: structure, stack, contribution rules,10local dev, quality gates, issues/PR workflow. Does not provision clusters or11operate remote environments unless asked.1213## When to Use1415"How do I contribute to Cloud Pi Native console?" / "What is the console backend16target and repo layout?" / "What commands for console local dev, lint, tests?" /17"What is the CPiN PR/commit/review workflow?" / "Implement/fix something in18server-nestjs (issue-first, module-consistent, tested)" / "Write a vitest or e2e19spec for a server-nestjs module".2021## Échelle de coordination (stack → async → swarm)2223Choisis l'outil de coordination par nombre d'unités et infrastructure avant de24créer la branche ; l'échelle prend le minimum qui convient :2526- **Une unité** → `cpn-stack` — workspace jj frais épinglé sur `main@origin`,27 bookmark limité à ce workspace. Défaut pour un correctif isolé ; obligatoire28 si le checkout contient du WIP concurrent à ne pas mélanger.29- **N unités parallèles, un dépôt** → `cpn-async` — un workspace par unité, DAG30 depth/join via `jj new <a> <b>`, PR indépendantes ou en chaîne ; contrats31 partagés figés avant fan-out.32- **Unités exigeant capacités ou machines différentes** → `cpn-swarm` — routage33 A2A par capacité, machine et pression runner ; jamais pour quelques PR sœurs34 dans un dépôt (c'est `cpn-async`).35- **Flux unique, checkout propre** → aucun skill de coordination ; boucle36 normale ci-dessous.3738L'escalade est à sens unique : `cpn-stack` → `cpn-async` → `cpn-swarm`. Pas de39swarm pour une unité, pas de fan-out avant le ledger d'issue réglé.4041## Phases4243Work-item lifecycle; gate phases are the mechanical walls a change must clear.4445| # | Phase | Owner | Gate |46| --- | -------------------------------------------------------------- | ------------------------------------ | ----------------- |47| 0 | Discussion (RFC) — only if the problem isn't converged | `cpn-discussion` | entry |48| 1 | Issue — French problem statement + `Définition du fini` ledger | `cpn-issue` | ledger set |49| 2 | Triage — labels/assignee/milestone/project/reviewers | `cpn-issue-triage` / `cpn-pr-triage` | ledger settled |50| 3 | Branch + implement (jj workspace, conventional commits) | this skill | — |51| 4 | Commit (conventional, SSH-signed) | `cpn-commit` | commit shape |52| 5 | Code review (adversarial pre-merge) | `cpn-pr-review` | review gate |53| 6 | PR (origin-only draft, link `Issues liées`) | `cpn-pr` | — |54| 7 | Land (`gh pr merge --squash` + merge queue) | this skill | branch protection |55| 8 | Close deliberately (verify N of N) | `cpn-issue` | ledger discharged |5657Phases 2 and 5 are the before-code / before-merge gates. The console `Procedure`58(steps 1–12) implements phases 3–7.5960## Prerequisites6162- Local checkout at `~/Source/Repos/github.com/cloud-pi-native`63- Docker >= 27 (compose >= 2.35, buildx), Node.js >= 24, pnpm >= 106465## How to Run6667Use `read_file` / `search_files` on the repo paths; run console commands via68`terminal` from the `console` dir.6970Parallelize independent, non-overlapping module changes with `delegate_task`71(e.g. implementation + its vitest spec, or two unrelated modules) as72self-contained subagents, each given this skill's context and the target repo73path. Keep dependent steps (typecheck/test after code) in the same task. Fix74shared contracts (interfaces, exported types, file ownership) before fan-out. A75subagent's "done" is self-certification: after merging, the dispatcher re-runs76each leaf's checks itself via `terminal`.7778```python79delegate_task(tasks=[80 {"goal": "Implement <module> in <org>/<repo>: <contract>. Run <lint/test> "81 "and confirm green before reporting done. Keep dependent "82 "typecheck/test in the same task.",83 "context": "cpn console repo; one workspace per unit per cpn-async; "84 "contracts fixed before fan-out.",85 "toolsets": ["terminal", "file"]},86])87```8889## Quick Reference9091- `console/README.md` — overview, architecture, ports, run modes92- `console/CONTRIBUTING.md` — scope, backend target, quality gates93- `console/package.json` — workspace scripts: lint, test, build, docker94- `console/.github/PULL_REQUEST_TEMPLATE.md` — required PR sections95- `console/apps/server-nestjs` — current backend target; `console/apps/server` —96 historical, **do not modify**97- `console/misc/plugins.md` — plugin lifecycle; `console/playwright/README.md` —98 Playwright e2e99100## Procedure1011021. Read `console/CONTRIBUTING.md` before changing backend/dependency behavior.1032. Backend target is `apps/server-nestjs`; never touch `apps/server`.1043. **Issue-first is mandatory.** Lifecycle: discussion → issue → issue comments105 → PR. Unclear problem → open a Discussion RFC first (`cpn-discussion`). Issue106 body = **problem statement** (need, scope, API/data/security impact, `- [ ]`107 acceptance tasklist — see Gates), never the solution; analysis goes in108 comments. One issue per item. Create via `cpn-issue` if absent; link PR with109 `Refs #N` (fermer délibérément après N-sur-N, voir `references/pitfalls.md`).110 No bare-request implementation; no PR without an issue behind it.111112 Reference-safety: `#N` in a PR/commit body resolves to console issue/PR **N**113 and `Closes` / `Fixes` / `Resolves` auto-close it on merge. Bare `#N` is only114 safe for a console ticket; cross-repo refs use a full URL or `owner/repo#N`.1154. **Triage before work** (`cpn-issue-triage`): assign every exposed metadata —116 labels (conventional-prefix → type), assignee (active `gh` identity),117 milestone (bug → current patch, feature → next release), project if obvious,118 reviewers. Apply only empty, determinable fields; never invent a label.1195. From `console`: `pnpm install`, `pnpm build`,120 `pnpm --filter @cpn-console/server-nestjs run db:generate`.1216. Launch: local+remote → `pnpm run dev` then122 `pnpm --filter @cpn-console/server-nestjs run dev` +123 `pnpm --filter @cpn-console/client run dev`; full containerized →124 `pnpm run docker:dev`; integration → `pnpm run docker:integ` or125 `pnpm run integ`.1267. Checks before submit: `pnpm lint`, `pnpm test`, `pnpm playwright:test` if a127 journey is affected.1288. Fresh jj workspace per item (une unité → `cpn-stack`, voir Échelle de129 coordination) : `jj workspace add -m <repo>.<name> . ../<repo>.<name>` (or130 `jj workspace add --revision <base> <path>`). Build a **stack of small child131 commits**; branch out with `jj new <other-parent>` when a commit doesn't need132 its parent. Multiple children of one parent → jj **diamond** (natively133 tracked, parallelizes landing). Fully independent streams → own134 `jj workspace add ../<repo>.<name> --name <repo>.<name>` at `@` and own a135 **standalone** PR (`cpn-async`: fan-out, join `jj new <a> <b>`, land via136 standalone `gh pr`).137 Don't stack unless a later module imports an earlier one's new code.1389. Conventional English commits, one per unit. **jj-backed — never139 `git commit`**; use `jj describe -m "msg"` / `jj new -m "msg"`. Fold into140 existing: `jj log -r '::@'`; if covered,141 `jj squash -m "msg" -f @ -t <existing>` instead of a new commit. Detect jj142 via `jj status` / `.jj/`; `git reflog` showing `export from jj` means143 jj-owned.14410. Before PR: (a) conflict-check children — `jj log -r '::@'`, rebase each onto145 parent (`jj rebase -d <parent> -r <child>`); 2-sided `<<<<<<<` resolves in146 WC (`jj status` clears). A `<name> (conflicted)` bookmark is a remote147 tracking conflict — surface it, don't `jj bookmark set`. (b) Re-describe:148 `jj log -r '@-|@'`; if a squash merged concerns,149 `jj describe -r <rev> -m "msg"`.15011. Open **DRAFT** PR via `.github/PULL_REQUEST_TEMPLATE.md`:151152```bash153gh pr create --draft --fill --body "Refs #N"154```155156Don't mark ready until review passes; WIP at handoff → leave draft + say so157(migration PRs draft unless told). 12. **Code review before merge**158(`cpn-pr-review`): adversarial over diff — architecture, conventional-commit +159French rules, trust-boundary security, root-cause vs symptom. The review is the160gate; don't mark ready until findings resolved or explicitly waived.161162## Validate assumptions before work163164Probe each requirement and RECORD the result; an unmet requirement is a reported165blocker, never a silent scope change:166167- gh identity + write: `gh api user --jq .login` and168 `gh api repos/cloud-pi-native/console --jq .viewerPermission` — need169 `write`/`admin` to push to origin.170- Toolchain: `node --version` (≥24), `pnpm --version`; jj present (console is171 jj-backed — never `git commit`; use `jj describe`/`jj new`).172- The issue exists (issue-first; create via `cpn-issue` if not).173174Report shape: `BLOCKED: <requirement> — <evidence> — <recovery path>`. Unblocked175streams may fan out (`cpn-async`); the blocked stream is surfaced.176177## Landing and PR↔commit parity178179Landing follows the same origin-only discipline as the other cpn skills:180181- **Origin-only.** Clone the org repo so `origin` is `cloud-pi-native/*`, push182 the branch to `origin`, and open the PR with183 `--head cloud-pi-native:<branch>`. (Pre-2026-08 `--head shikanime:<branch>`184 guidance is retired.)185- **Plain `gh pr` is the landing path** (the org removed the `gh stack`186 extension). Before opening each PR run the `cpn-pr` duplicate/stack check187 (step 1b): no new PR if an open one already delivers the change; stack on the188 existing PR's branch when your change depends on it. Open each branch with189 `gh pr create --draft --fill --body "Refs #N"` and land with190 `gh pr merge --squash`; parity is enforced by review, not tooling.191- **Une PR résout toujours une issue ; ne jamais l'ouvrir seule.** La liaison192 est **many-to-many** : plusieurs PR peuvent résoudre une issue ; une PR peut193 en servir plusieurs. Par défaut `Refs #N` sur chaque PR. Tout autre cas :194 après la fusion de la PR finale, vérifier la tâche N sur N et fermer195 délibérément (`gh issue close <N> -c "<evidence>"`).196- **Parity principle: the commit is the source of truth; the PR restates it.**197 The PR title must equal the commit subject and the PR body must restate the198 commit message — don't add new rationale the commit doesn't state (see199 `cpn-commit` / `cpn-pr`). Author the commit to carry the full rationale200 (subject + blank line + body) so the PR restates it without inventing claims.201202## Deep detail (gates, module design, vitest/e2e/Playwright rules)203204These are kept out of the always-loaded footprint; load on demand:205206- **Gates** (done-is-proven theory, tasklist-in-issue, wall, report audit):207 `references/dev-detail.md`208- **Implementation consistency** (module design, Prisma via `-queries.utils.ts`,209 config injection, hooks flags): `references/dev-detail.md`210- **Testing practice** (vitest spec rules, mockDeep, faker, no describe-scope211 calls) and **E2E / Playwright** requirements: `references/dev-detail.md`212213## Migration PR review (Fastify → server-nestjs)214215A migration PR can pass typecheck/lint/unit yet still ship a **silent sync216regression**: the new service emits domain events via `EventEmitter2` but217nothing bridges them into the plugin hook system, so Keycloak/GitLab group syncs218stop at cutover. Before approving, run the parity checklist in219`references/migration-parity-checklist.md` (grep emitted events vs `@OnEvent`220consumers vs legacy `hook.<entity>` calls). Core rule: every221`eventEmitter.emitAsync('<entity>.<verb>')` must have a corresponding222`@OnEvent('<entity>.<verb>')` → `capturePluginResult` handler, or the cutover is223blocked.224225## Pitfalls226227Optional edge cases and gotchas — load `references/pitfalls.md` on demand.228229## Verification230231Run `read_file` on `console/CONTRIBUTING.md` and confirm it states232`apps/server-nestjs` as the backend target and lists `pnpm lint`, `pnpm test`,233and `pnpm playwright:test` as pre-submission checks.234235## See also236237- `cpn-commit` — the commit shape (conventional subject, author identity, SSH238 signing) this workflow lands.239- `cpn-pr` — origin-only PR opening from these commits.240- `cpn-stack` — isolation d'une unité en workspace frais.241- `cpn-swarm` — cluster d'agents A2A.