# Cpn Dev Workflow

> À utiliser quand vous travaillez dans le dépôt console cloud-pi-native/console : règles de contribution, cycle de dev et workflow PR.

- Skill: `shikanime-labs/cpn-dev-workflow` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add shikanime-labs/cpn-dev-workflow`
- Raw SKILL.md: https://api.skillmd.com/api/skills/shikanime-labs/cpn-dev-workflow/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: Apache-2.0
- Author: shikanime-labs (https://skillmd.com/u/shikanime-labs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/shikanime-labs/cpn-dev-workflow

---


# 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`.

```python
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

1. Read `console/CONTRIBUTING.md` before changing backend/dependency behavior.
2. Backend target is `apps/server-nestjs`; never touch `apps/server`.
3. **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`.
4. **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.
5. From `console`: `pnpm install`, `pnpm build`,
   `pnpm --filter @cpn-console/server-nestjs run db:generate`.
6. 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`.
7. Checks before submit: `pnpm lint`, `pnpm test`, `pnpm playwright:test` if a
   journey is affected.
8. 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.
9. 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.
10. 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"`.
11. Open **DRAFT** PR via `.github/PULL_REQUEST_TEMPLATE.md`:

```bash
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.

