From fff123e40243d38419d0b56793a13b05da98cde5 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:25:29 +0200 Subject: [PATCH 1/2] docs: add repo-owned agent skills Signed-off-by: William Phetsinorath Change-Id: I16ed8b7b82081dfb93609c317a87107b6a6a6964 --- .agents/skills/commit/SKILL.md | 49 +++++++++++++++++++ .agents/skills/dev-workflow/SKILL.md | 69 ++++++++++++++++++++++++++ .agents/skills/pull-request/SKILL.md | 73 ++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 .agents/skills/commit/SKILL.md create mode 100644 .agents/skills/dev-workflow/SKILL.md create mode 100644 .agents/skills/pull-request/SKILL.md diff --git a/.agents/skills/commit/SKILL.md b/.agents/skills/commit/SKILL.md new file mode 100644 index 0000000000..4820773e09 --- /dev/null +++ b/.agents/skills/commit/SKILL.md @@ -0,0 +1,49 @@ +--- +name: commit +description: + "Use when committing in this repo: conventional commit shape enforced by + commitlint, authored through jj." +version: 1.0.0 +license: Apache-2.0 +--- + +# Console commits + +The `commit-msg` Husky hook runs commitlint (`commitlint.config.cjs`, extends +`@commitlint/config-conventional` with `'body-leading-blank': [2, 'always']`). +Release Please derives version bumps from the type. + +## Commit shape + +| Rule | Value | +| -------- | ----------------------------------------------------------------------------------------- | +| Types | `feat`, `fix`, `chore`, `docs`, `refactor`, `revert`, `build` (`feature` also recognized) | +| Scope | optional, `type(scope):` | +| Breaking | `type!:` / `type(scope)!:` (MAJOR bump) | +| Subject | imperative, lowercase start, no trailing period | +| Body | optional, separated from the subject by exactly one blank line | +| Footer | `Refs #N`; never `Closes #N` — issues close deliberately after verification | + +Reference safety: a bare `#N` resolves to a console issue/PR. Cross-repo +references use a full URL or `owner/repo#N`. + +## Procedure (jj — never `git commit`) + +```bash +jj describe -m "fix: prevent null group lookup in keycloak sync" +``` + +With a body — each `-m` block is a paragraph, so the blank line between blocks +satisfies `body-leading-blank`: + +```bash +jj describe \ + -m "feat(plugins): add vault secret rotation" \ + -m "Supports monthly rotation via the hook post step." \ + -m "Refs #123" +``` + +To fold work into the current commit instead of stacking a new one: +`jj squash` from the working copy, or keep amending `@` with `jj describe`. + +Confirm: `jj log -r @ --no-graph -T description`. diff --git a/.agents/skills/dev-workflow/SKILL.md b/.agents/skills/dev-workflow/SKILL.md new file mode 100644 index 0000000000..3972dc7959 --- /dev/null +++ b/.agents/skills/dev-workflow/SKILL.md @@ -0,0 +1,69 @@ +--- +name: dev-workflow +description: + "Use when contributing to this repo: issue-first lifecycle, workspace + isolation, quality gates, and the PR workflow." +version: 1.0.0 +license: Apache-2.0 +--- + +# Console dev workflow + +## Stack + +- pnpm monorepo, Node >= 26, pnpm >= 11.8 +- Backend target: `apps/server-nestjs`. `apps/server` is frozen (read-only + reference) — never modify it. +- jj-backed repository: the working copy is a commit. Never `git commit`; + describe with `jj describe` / `jj new`, inspect with `jj log` / `jj diff`. + +## Lifecycle (issue-first, mandatory) + +Lifecycle: discussion → issue → issue comments → PR. No PR without an issue +behind it; no bare-request implementation. + +1. **One issue per item.** Bug `🐛 [BUG] - ` / feature + `💡 [REQUEST] - `, via `.github/ISSUE_TEMPLATE/`. Body = problem + statement (need, scope, impact) plus a `- [ ]` acceptance tasklist + (Définition du fini) — never the solution; analysis goes in comments. + Search existing issues before creating. +2. **Triage before work**: set each empty, determinable field — labels + filtered against `gh label list` (never invent one), assignee, milestone + (bug → highest open patch of the current minor line; feature → next + minor/major). +3. **Branch from `origin/main`**, implement, commit (see the `commit` skill). +4. **Draft PR** linked to the issue (see the `pull-request` skill). +5. **Human approving review is the merge gate** — do not self-merge. +6. **Close deliberately**: verify every acceptance box, then close the issue + with an evidence comment. Never rely on PR-merge auto-close. + +## Isolation + +- One logical change per branch and PR; out-of-scope fixes become follow-up + issues. +- When the current checkout holds unrelated work in progress, isolate in a + fresh jj workspace instead of mixing: + +```bash +jj workspace add ../console. -r 'main@origin' +``` + +## Quality gates (before any PR) + +```bash +pnpm format +pnpm lint +pnpm test # targeted specs at minimum +pnpm playwright:test # only if a user journey is affected +``` + +## Validate the environment before work + +```bash +gh api user --jq .login +gh api repos/cloud-pi-native/console --jq .viewerPermission # need write +node --version && pnpm --version +jj status +``` + +An unmet requirement is a reported blocker, never a silent scope change. diff --git a/.agents/skills/pull-request/SKILL.md b/.agents/skills/pull-request/SKILL.md new file mode 100644 index 0000000000..9088a9c2ee --- /dev/null +++ b/.agents/skills/pull-request/SKILL.md @@ -0,0 +1,73 @@ +--- +name: pull-request +description: + "Use when opening or triaging a PR in this repo: French body from the + template, draft-first, origin-only, review-gated." +version: 1.0.0 +license: Apache-2.0 +--- + +# Console pull requests + +## Before opening + +- Duplicate/stack check — list open PRs; if one already delivers the change, + push there instead of opening a second. If your change depends on an open + PR, base your branch on its branch. + +```bash +gh pr list --state open --json number,title,headRefName \ + --jq '.[] | "\(.number)\t\(.title)\t\(.headRefName)"' +``` + +- Rebase onto `main` first (`jj rebase -d main`); never push a conflicted + branch. +- Quality gates green (see the `dev-workflow` skill); the `pre-push` Husky + hook already runs unit tests. + +## Opening + +- Branches live on the org repo itself (origin-only, no forks). `main` is + protected; only `hotfix/*` may bypass the feature-branch rule, and the + branch prefix matches the commit type (`feat/`, `fix/`, `docs/`, ...). +- Open as **draft**, title = conventional commit subject, body = + `.github/PULL_REQUEST_TEMPLATE.md` verbatim, in French, linking the issue + under `Issues liées`: + +```bash +gh pr create --repo cloud-pi-native/console --draft --base main \ + --head \ + --title ": " \ + --body-file +``` + +- The body is free text: natural prose, no hard wrapping, never run a + formatter over it. +- A literal `@` in prose triggers a user/team mention — wrap it in a code + span (inline or fenced). + +## Triage after creation + +Set each empty, determinable field, additively (`--add-label` / +`--add-assignee`, never `--label`): + +- **labels** — from `gh label list`, never from memory. Conventional type → + matching label; doc changes → `docs` (this repo's real label; + `documentation` does not exist). +- **assignee** — the author, if empty. +- **milestone** — bug → highest open patch of the current minor line; + feature → next minor/major. +- **reviewers** — one collaborator if none requested; otherwise skip. + +## After opening + +- Keep it draft until review passes; a human approving review from another + collaborator is required — never self-merge. +- The PR title and body restate the commit: the commit is the source of + truth; do not add rationale the commit does not carry. +- CI includes the SonarQube Quality Gate (0 new issues required). When all + checks are green but `mergeStateStatus` is `BLOCKED`, enqueue: + +```bash +gh workflow run 243523481 --repo cloud-pi-native/console -f PR_NUMBER= +``` From 3aa337c763b5044aa8e3dd54f6d2d60922f459da Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:39:32 +0200 Subject: [PATCH 2/2] docs: generalize agent skills to git and broaden to issue and review Drop the jj-specific commands in favor of plain git (worktree isolation, heredoc commit bodies, rebase onto origin/main). Add cpn-issue (French templates, acceptance ledger, triage) and cpn-review (severity-tagged inline findings, DoD reconciliation, thread resolution), both distilled from the team's shared review practice. Rename all skills to the cpn- prefix to mirror the org catalog. No jj anywhere. Refs #2710 --- .../skills/{commit => cpn-commit}/SKILL.md | 49 +++++++---- .../SKILL.md | 60 ++++++------- .agents/skills/cpn-issue/SKILL.md | 84 +++++++++++++++++++ .../skills/{pull-request => cpn-pr}/SKILL.md | 35 +++++--- .agents/skills/cpn-review/SKILL.md | 79 +++++++++++++++++ 5 files changed, 252 insertions(+), 55 deletions(-) rename .agents/skills/{commit => cpn-commit}/SKILL.md (53%) rename .agents/skills/{dev-workflow => cpn-dev-workflow}/SKILL.md (52%) create mode 100644 .agents/skills/cpn-issue/SKILL.md rename .agents/skills/{pull-request => cpn-pr}/SKILL.md (66%) create mode 100644 .agents/skills/cpn-review/SKILL.md diff --git a/.agents/skills/commit/SKILL.md b/.agents/skills/cpn-commit/SKILL.md similarity index 53% rename from .agents/skills/commit/SKILL.md rename to .agents/skills/cpn-commit/SKILL.md index 4820773e09..ea7e8ef622 100644 --- a/.agents/skills/commit/SKILL.md +++ b/.agents/skills/cpn-commit/SKILL.md @@ -1,8 +1,8 @@ --- -name: commit +name: cpn-commit description: "Use when committing in this repo: conventional commit shape enforced by - commitlint, authored through jj." + commitlint." version: 1.0.0 license: Apache-2.0 --- @@ -13,13 +13,23 @@ The `commit-msg` Husky hook runs commitlint (`commitlint.config.cjs`, extends `@commitlint/config-conventional` with `'body-leading-blank': [2, 'always']`). Release Please derives version bumps from the type. +## Prerequisites + +- Feature branch off `origin/main`, `git branch --show-current` to confirm — + never commit on `main`. +- Husky hooks active via `pnpm install`; commitlint rejects a malformed + message at `commit-msg`. + +An unmet requirement is a reported blocker, never a silent scope change. +Never bypass hooks with `--no-verify`. + ## Commit shape | Rule | Value | | -------- | ----------------------------------------------------------------------------------------- | -| Types | `feat`, `fix`, `chore`, `docs`, `refactor`, `revert`, `build` (`feature` also recognized) | +| Types | `feat`, `fix`, `chore`, `docs`, `refactor`, `revert`, `build`, `feature` | | Scope | optional, `type(scope):` | -| Breaking | `type!:` / `type(scope)!:` (MAJOR bump) | +| Breaking | `type!:` / `type(scope)!:` | | Subject | imperative, lowercase start, no trailing period | | Body | optional, separated from the subject by exactly one blank line | | Footer | `Refs #N`; never `Closes #N` — issues close deliberately after verification | @@ -27,23 +37,32 @@ Release Please derives version bumps from the type. Reference safety: a bare `#N` resolves to a console issue/PR. Cross-repo references use a full URL or `owner/repo#N`. -## Procedure (jj — never `git commit`) +## Procedure + +Single-line message: ```bash -jj describe -m "fix: prevent null group lookup in keycloak sync" +git commit -m "fix: prevent null group lookup in keycloak sync" ``` -With a body — each `-m` block is a paragraph, so the blank line between blocks -satisfies `body-leading-blank`: +With a body, use a heredoc; repeated `-m` flags are fragile under shell +quoting. The blank line after the subject satisfies `body-leading-blank`: ```bash -jj describe \ - -m "feat(plugins): add vault secret rotation" \ - -m "Supports monthly rotation via the hook post step." \ - -m "Refs #123" +git commit -m "$(cat <<'EOF' +feat(plugins): add vault secret rotation + +Supports monthly rotation via the hook post step. + +Refs #123 +EOF +)" ``` -To fold work into the current commit instead of stacking a new one: -`jj squash` from the working copy, or keep amending `@` with `jj describe`. +Fold work into the last commit with `git commit --amend` — never amend a +commit that is already pushed and under review. + +## Verify -Confirm: `jj log -r @ --no-graph -T description`. +`git log -1 --format=%B` — shape matches the table; footer `Refs #N` present +when an issue stands behind the commit. diff --git a/.agents/skills/dev-workflow/SKILL.md b/.agents/skills/cpn-dev-workflow/SKILL.md similarity index 52% rename from .agents/skills/dev-workflow/SKILL.md rename to .agents/skills/cpn-dev-workflow/SKILL.md index 3972dc7959..6c56c46103 100644 --- a/.agents/skills/dev-workflow/SKILL.md +++ b/.agents/skills/cpn-dev-workflow/SKILL.md @@ -1,5 +1,5 @@ --- -name: dev-workflow +name: cpn-dev-workflow description: "Use when contributing to this repo: issue-first lifecycle, workspace isolation, quality gates, and the PR workflow." @@ -14,56 +14,60 @@ license: Apache-2.0 - pnpm monorepo, Node >= 26, pnpm >= 11.8 - Backend target: `apps/server-nestjs`. `apps/server` is frozen (read-only reference) — never modify it. -- jj-backed repository: the working copy is a commit. Never `git commit`; - describe with `jj describe` / `jj new`, inspect with `jj log` / `jj diff`. +- Git-backed repository: work on feature branches off `origin/main`; + `main` is protected. -## Lifecycle (issue-first, mandatory) +## Prerequisites + +```bash +gh api user --jq .login # authenticated +gh api repos/cloud-pi-native/console --jq .viewerPermission # need write +node --version && pnpm --version # Node >= 26, pnpm >= 11.8 +git status --porcelain # clean checkout +``` + +An unmet requirement is a reported blocker, never a silent scope change. + +## Lifecycle Lifecycle: discussion → issue → issue comments → PR. No PR without an issue behind it; no bare-request implementation. 1. **One issue per item.** Bug `🐛 [BUG] - ` / feature `💡 [REQUEST] - `, via `.github/ISSUE_TEMPLATE/`. Body = problem - statement (need, scope, impact) plus a `- [ ]` acceptance tasklist - (Définition du fini) — never the solution; analysis goes in comments. - Search existing issues before creating. + statement plus a `- [ ]` acceptance tasklist, Définition du fini — never + the solution; analysis goes in comments. Search existing issues before + creating. 2. **Triage before work**: set each empty, determinable field — labels - filtered against `gh label list` (never invent one), assignee, milestone - (bug → highest open patch of the current minor line; feature → next - minor/major). -3. **Branch from `origin/main`**, implement, commit (see the `commit` skill). -4. **Draft PR** linked to the issue (see the `pull-request` skill). + from `gh label list`, never invented; assignee; milestone: bug → highest + open patch of the current minor line, feature → next minor/major. +3. **Branch from `origin/main`**, implement, commit. +4. **Draft PR** linked to the issue. 5. **Human approving review is the merge gate** — do not self-merge. 6. **Close deliberately**: verify every acceptance box, then close the issue with an evidence comment. Never rely on PR-merge auto-close. +Details live in the `cpn-issue`, `cpn-commit`, `cpn-pr`, and `cpn-review` +skills. + ## Isolation - One logical change per branch and PR; out-of-scope fixes become follow-up issues. - When the current checkout holds unrelated work in progress, isolate in a - fresh jj workspace instead of mixing: + fresh git worktree instead of mixing: ```bash -jj workspace add ../console. -r 'main@origin' +git worktree add ../console. -b origin/main ``` -## Quality gates (before any PR) +## Verify + +Before opening the PR: ```bash pnpm format pnpm lint -pnpm test # targeted specs at minimum -pnpm playwright:test # only if a user journey is affected +pnpm test # targeted specs at minimum +pnpm playwright:test # only if a user journey is affected ``` - -## Validate the environment before work - -```bash -gh api user --jq .login -gh api repos/cloud-pi-native/console --jq .viewerPermission # need write -node --version && pnpm --version -jj status -``` - -An unmet requirement is a reported blocker, never a silent scope change. diff --git a/.agents/skills/cpn-issue/SKILL.md b/.agents/skills/cpn-issue/SKILL.md new file mode 100644 index 0000000000..d68cb8d3fe --- /dev/null +++ b/.agents/skills/cpn-issue/SKILL.md @@ -0,0 +1,84 @@ +--- +name: cpn-issue +description: + "Use when opening, triaging, or closing an issue in this repo: French + templates, acceptance ledger, additive triage." +version: 1.0.0 +license: Apache-2.0 +--- + +# Console issues + +Issue-first repo norm: no PR without an issue behind it (lifecycle in the +`cpn-dev-workflow` skill, link-up in `cpn-pr`). + +## Prerequisites + +- Issues live on `cloud-pi-native/console`. +- `gh` authenticated with write access, verified: + +```bash +gh api user --jq .login # authenticated +gh api repos/cloud-pi-native/console --jq .viewerPermission # write to triage +``` + +An unmet requirement is a reported blocker, never a silent scope change. + +## Open + +1. Search before creating — reuse a matching open issue instead of a + duplicate: `gh issue list --repo cloud-pi-native/console --state open + --search ""`. +2. Title `🐛 [BUG] - ` or `💡 [REQUEST] - `, via + `.github/ISSUE_TEMPLATE/`; label `bug` / `enhancement`. +3. Body in **French**, from the template: problem statement (need, scope, + impact) plus a `- [ ]` **Définition du fini** acceptance tasklist as the + work ledger — never the solution; findings and analysis go in comments. +4. Free-text rules: natural paragraphs, no hard wrapping, never run a + formatter over a body; a literal `@` in prose triggers a user/team mention + — wrap it in a code span. + +```bash +gh issue create --repo cloud-pi-native/console \ + --title "💡 [REQUEST] - " --label enhancement --body-file +``` + +## Triage + +Fill each empty, determinable field, additively (`--add-label` / +`--add-assignee`, never `--label`); never invent a value the repo doesn't +have — filter labels against `gh label list`: + +```bash +gh issue edit --repo cloud-pi-native/console \ + --add-label