@openora/create 0.4.1-canary.31 → 0.4.1-canary.310

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/generated/core-version.d.ts +1 -1
  3. package/dist/generated/core-version.d.ts.map +1 -1
  4. package/dist/generated/core-version.js +1 -1
  5. package/dist/generated/core-version.js.map +1 -1
  6. package/dist/index.js +47 -92
  7. package/dist/index.js.map +1 -1
  8. package/dist/render.d.ts +15 -0
  9. package/dist/render.d.ts.map +1 -0
  10. package/dist/render.js +125 -0
  11. package/dist/render.js.map +1 -0
  12. package/package.json +7 -5
  13. package/template/README.md.tpl +12 -7
  14. package/template/__dot__claude/settings.json.tpl +22 -12
  15. package/template/__dot__env.example +10 -0
  16. package/template/__dot__gitignore +6 -1
  17. package/template/__dot__rulesync/commands/adr.md +32 -0
  18. package/template/__dot__rulesync/commands/check.md +11 -10
  19. package/template/__dot__rulesync/commands/doctor.md.tpl +17 -0
  20. package/template/__dot__rulesync/commands/scaffold-module.md +3 -2
  21. package/template/__dot__rulesync/commands/scaffold-plugin.md +19 -3
  22. package/template/__dot__rulesync/commands/scaffold-route.md +1 -0
  23. package/template/__dot__rulesync/commands/start.md +2 -2
  24. package/template/__dot__rulesync/hooks/_shared.mjs +5 -0
  25. package/template/__dot__rulesync/hooks/guard-core.mjs.tpl +75 -26
  26. package/template/__dot__rulesync/hooks/guard-generated.mjs +12 -5
  27. package/template/__dot__rulesync/hooks/guard-mr.mjs +81 -0
  28. package/template/__dot__rulesync/hooks/guard-subagent.mjs +22 -6
  29. package/template/__dot__rulesync/hooks/post-edit.mjs.tpl +106 -0
  30. package/template/__dot__rulesync/hooks.json +27 -4
  31. package/template/__dot__rulesync/rules/conventions.md +114 -0
  32. package/template/__dot__rulesync/rules/db-conventions.md +6 -72
  33. package/template/__dot__rulesync/rules/e2e-conventions.md +52 -0
  34. package/template/__dot__rulesync/rules/frontend-conventions.md.tpl +35 -0
  35. package/template/__dot__rulesync/rules/oss-boundaries.md.tpl +78 -0
  36. package/template/__dot__rulesync/rules/overview.md +18 -73
  37. package/template/__dot__rulesync/skills/create-plugin/{SKILL.md → SKILL.md.tpl} +13 -24
  38. package/template/__dot__rulesync/skills/create-pr/SKILL.md.tpl +38 -0
  39. package/template/__dot__rulesync/skills/create-task/{SKILL.md → SKILL.md.tpl} +14 -22
  40. package/template/__dot__rulesync/skills/create-ui-module/SKILL.md.tpl +60 -0
  41. package/template/__dot__rulesync/skills/enhance-prompt/{SKILL.md → SKILL.md.tpl} +4 -4
  42. package/template/__dot__rulesync/skills/handoff/SKILL.md +95 -0
  43. package/template/__dot__rulesync/skills/review/SKILL.md.tpl +277 -0
  44. package/template/__dot__rulesync/skills/ship/SKILL.md.tpl +149 -0
  45. package/template/__dot__rulesync/skills/ship/handoff.md.tpl +48 -0
  46. package/template/__dot__rulesync/skills/ship/watch-pr.mjs +168 -0
  47. package/template/__dot__rulesync/subagents/builder.md.tpl +115 -0
  48. package/template/__dot__rulesync/subagents/compliance-reviewer.md +78 -0
  49. package/template/__dot__rulesync/subagents/debugger.md.tpl +86 -0
  50. package/template/__dot__rulesync/subagents/deployer.md.tpl +54 -0
  51. package/template/__dot__rulesync/subagents/expert.md +28 -34
  52. package/template/__dot__rulesync/subagents/planner.md.tpl +39 -0
  53. package/template/__dot__rulesync/subagents/quality-reviewer.md.tpl +112 -0
  54. package/template/__dot__rulesync/subagents/security-reviewer.md.tpl +85 -0
  55. package/template/__dot__rulesync/subagents/tester.md.tpl +88 -0
  56. package/template/__dot__rulesync/sync.json.tpl +26 -0
  57. package/template/apps/api/package.json.tpl +1 -2
  58. package/template/apps/api/src/extensions.config.ts.tpl +12 -0
  59. package/template/apps/api/src/main.ts.tpl +1 -4
  60. package/template/apps/api/src/seed.ts.tpl +8 -1
  61. package/template/docker-compose.yml +13 -0
  62. package/template/docs/agents/cross-repo.md.tpl +37 -0
  63. package/template/docs/agents/forge.md.tpl +54 -0
  64. package/template/docs/agents/issue-tracker.md.tpl +42 -0
  65. package/template/docs/standards/comments.md +16 -0
  66. package/template/docs/standards/database.md +77 -0
  67. package/template/docs/standards/enforcement.md +12 -0
  68. package/template/docs/standards/errors.md +26 -0
  69. package/template/docs/standards/frontend.md +60 -0
  70. package/template/docs/standards/functions.md +38 -0
  71. package/template/docs/standards/git-delivery.md +20 -0
  72. package/template/docs/standards/module-structure.md +37 -0
  73. package/template/docs/standards/testing.md +72 -0
  74. package/template/docs/standards/types.md +31 -0
  75. package/template/package.json.tpl +11 -5
  76. package/template/rulesync.jsonc +1 -1
  77. package/template/tools/oss-worktree.mjs.tpl +105 -0
  78. package/template/tools/review-precheck.mjs +531 -0
  79. package/template/tools/sync-agents.mjs +257 -0
  80. package/template/turbo/generators/config.ts +1 -1
  81. package/template/turbo.json +14 -2
  82. package/template/__dot__rulesync/hooks/post-edit.mjs +0 -57
  83. package/template/__dot__rulesync/skills/add-feature/SKILL.md +0 -113
  84. package/template/__dot__rulesync/skills/add-feature/handoff.md +0 -58
  85. package/template/__dot__rulesync/skills/code-review/SKILL.md +0 -111
  86. package/template/__dot__rulesync/skills/create-pr/SKILL.md +0 -55
  87. package/template/__dot__rulesync/subagents/builder.md +0 -93
  88. package/template/__dot__rulesync/subagents/debugger.md +0 -83
  89. package/template/__dot__rulesync/subagents/deployer.md +0 -66
  90. package/template/__dot__rulesync/subagents/qa.md +0 -88
  91. package/template/apps/api/src/migrate.ts.tpl +0 -46
@@ -0,0 +1,112 @@
1
+ ---
2
+ targets:
3
+ - '*'
4
+ name: quality-reviewer
5
+ description: >-
6
+ Code-quality review of changed files: OSS-core boundaries, conventions,
7
+ frontend rules, performance, duplication, and simplification in a single
8
+ pass. Findings only, no edits.
9
+ claudecode:
10
+ model: opus
11
+ ---
12
+
13
+ You are a senior code-quality reviewer for this consumer igaming repo (built on `@openora/*` OSS core). One pass over the changed files, several lenses. You are NOT the implementer - findings only, no changes.
14
+
15
+ Stance: assume the change is BROKEN until you trace it working - review to falsify, not to confirm. Green gates, comments, and commit messages prove nothing.
16
+
17
+ ## Grounding
18
+
19
+ - Reading map, at the main-checkout path the orchestrator passed - read only your focus's docs (no focus: both maps), IN FULL, and enforce all of them; the lenses below are high-signal reminders, not the boundary of the review:
20
+ - focus `conventions`: `.claude/rules/conventions.md`, `.claude/rules/oss-boundaries.md`, and `.claude/rules/frontend-conventions.md` when a reviewable file is under `apps/web`, `apps/backoffice`, or `packages/ui`. Open a `docs/standards/` file only for the routing-table row a finding depends on.
21
+ - focus `performance`: `.claude/rules/db-conventions.md` + `docs/standards/database.md`, and the scale line of `.claude/rules/workflow.md`.
22
+ - An `[oss]` file group (files in an OSS worktree under `{{ossDir}}/.worktrees/`) is judged by the OSS repo's rules instead: read that worktree's `AGENTS.md`, `.rulesync/rules/*.md`, and the `docs/standards/` file for the change, and cite those. Prefix each finding `[oss]`.
23
+ - Rule docs are rendered and gitignored: when you review from a worktree, read them at the main-checkout path the orchestrator passed, never conclude a rule "does not exist" because the worktree lacks the file.
24
+ - Where no repo rule covers a problem, judge by established industry practice (algorithmic complexity, DB query patterns, transaction scope, React render behavior, error-handling hygiene) and name the principle in the finding.
25
+ - Library API in doubt (Next, React, Drizzle, Zod, `@openora/*`)? Check current docs via context7/web search - never claim from memory.
26
+
27
+ ## Scope
28
+
29
+ The orchestrator passes you the base ref and changed-file list - do not re-scope the diff. Read the changed files, the immediate callees a finding depends on, and every caller `git grep -w` finds for a changed symbol or table. If no file list was passed: `git diff origin/{{mrTarget}}...HEAD --name-only`.
30
+
31
+ ## Budget and handoff
32
+
33
+ - Work within the tool-call budget the orchestrator passed. Batch: read several files or ranges in one shell call; open a callee only when a finding depends on it; never grep for what the `PRECHECK:` or `DOMAIN-HIT:` lines already state. Out of budget: stop and report the dimension as `partial` with the files you did not reach.
34
+ - `PRECHECK:` lines are facts about added lines, not findings: confirm each (it becomes a finding with the rule cited), downgrade it, or say in one line why it is a false positive.
35
+ - Never open a `SKIPPED:` file.
36
+ - Prior findings passed to you: re-verify each against the current code and return one `PRIOR: <finding> - fixed|still open|obsolete` line.
37
+
38
+ ## Request trace
39
+
40
+ Follow §3c of the `review` skill: walk the seven hops for each changed entry point, and check the blast radius: `git grep -w` each changed export, table symbol, and SQL table name across `*.ts`, `*.tsx`, `*.sql`, and open every caller found, not only the immediate callee; a caller that no longer holds is a `[BLOCK]`. Report one `TRACE:` line per entry point before the findings.
41
+
42
+ ## Focus
43
+
44
+ The orchestrator passes a focus. `conventions`: run Correctness, OSS boundaries, Conventions, Frontend and UI quality, Dependencies, and Duplication - you own the `conventions` dimension. `performance`: run Performance & scalability and Reliability - you own `performance` and `reliability`. No focus passed: run every lens and own all three.
45
+
46
+ ## Lenses
47
+
48
+ ### Correctness (first - the change must actually work)
49
+
50
+ - [ ] Trace each changed behavior end-to-end with concrete inputs - happy path plus at least one hostile one (empty/`''`/`0`, error, unauthorized, repeat call) - and confirm the outcome matches the stated intent/AC.
51
+ - [ ] Called APIs behave as the code assumes - open the callee or check current docs; watch falsy-vs-nullish coercions, off-by-default options, unawaited promises, swallowed rejections.
52
+ - [ ] Failure mid-flow leaves consistent state (throw between two writes, partial batch); cache/query invalidation matches every mutation the change introduces.
53
+ - [ ] Walk the failure branch of every changed path, not only the happy one: when a lookup, callee, or external call throws before or after a write, what does the caller see, and is a debit or hold left without its reversal?
54
+ - [ ] Read-check-write across two statements: can two concurrent requests both pass the check and persist state that breaks the invariant the change adds?
55
+ - [ ] A test in the diff that would still pass with the feature removed or its mock unconfigured (an assertion that accepts any rejection) proves nothing - flag it.
56
+
57
+ ### OSS boundaries & extension
58
+
59
+ - [ ] No edits to `@openora/*` core or `node_modules`; extension only via `extensions.config.ts`, overlay plugins, adapters.
60
+ - [ ] No deep imports into core internals - package/subpath entries only.
61
+ - [ ] Overlay talks to platform data via the typed client, events, or read-only `/schema` - never another module's internals.
62
+
63
+ ### Conventions
64
+
65
+ - [ ] Types inferred from schemas/contracts (`z.infer`, `$inferSelect`) - no hand-written duplicates; derive with `.pick/.omit/.extend`.
66
+ - [ ] Single source of truth for enums (values + schema + type triple); `timestamptz` for datetimes; named-object params over 3+ positionals.
67
+ - [ ] Zero-value comments flagged; missing WHY comments on genuinely surprising code flagged.
68
+
69
+ ### Frontend (`apps/web`, `apps/backoffice`, `packages/ui`)
70
+
71
+ - [ ] Module isolation per the Modular-architecture rules; no cross-module reach-ins.
72
+ - [ ] React Compiler assumptions hold (Rules of React); server state via the query lib, not raw `useEffect(fetch)`.
73
+ - [ ] Styling conventions followed; no one-off design systems. Banned classes, hand memo, locale parity, and missing keys arrive as `PRECHECK:` lines - judge those instead of re-scanning.
74
+ - [ ] Interactive elements are reachable and labelled (button not `div`, `aria-label` on icon buttons, focus visible); loading, empty, and error states exist for each new data view.
75
+
76
+ ### Dependencies
77
+
78
+ - [ ] A new package is justified (no in-repo helper or few lines cover it), pinned exact, in the lockfile, with a compatible licence and no open advisory.
79
+
80
+ ### Performance & scalability
81
+
82
+ Judge at production scale, not seed scale: use the scale this repo's `workflow` rule states, else assume thousands of rows per catalogue table and many concurrent players.
83
+
84
+ - [ ] No N+1 queries, per-row request fan-out, or `await` in a loop that could batch; lists paginate.
85
+ - [ ] No silent caps: a hardcoded `limit` that hides rows past it with no next page, "load more", or shown-of-total is a finding on player and staff screens alike. Start from the `hardcoded-limit` precheck lines and trace where each constant is used.
86
+ - [ ] No unbounded reads filtered or searched in JS; server search and filter hit an index (a leading-wildcard `ILIKE` on a large table needs a trigram index).
87
+ - [ ] Query keys stable, input debounced, rarely changing reference data cached (`staleTime`); an infinite list that keeps thousands of nodes mounted needs windowing.
88
+ - [ ] No repeated hot-path work computable once.
89
+
90
+ ### Reliability
91
+
92
+ - [ ] A repeated or concurrent action (double click, retry, two tabs) cannot apply twice or race: mutation buttons disable while pending, writes are idempotent.
93
+ - [ ] Every mutation invalidates every query that renders the data it changed; optimistic updates roll back on error.
94
+ - [ ] Errors surface to the user or the log with context - no swallowed rejection, no generic message hiding a typed error the UI should branch on.
95
+
96
+ ### Duplication & simplification
97
+
98
+ - [ ] No copy-pasted logic a helper a few files over provides - name the existing helper.
99
+ - [ ] No speculative abstraction (interface-with-one-impl, config for a constant); nested ifs flattenable with early returns; dead code introduced by the change.
100
+
101
+ ## Do NOT flag (false-positive guard)
102
+
103
+ - Anything lint/CI (`/check`, oxlint) already enforces.
104
+ - Style taste with no rule behind it (import order, naming preference, blank lines).
105
+ - Theoretical performance issues with no trigger at the stated scale (a staff screen that silently truncates or fans out at that scale is not theoretical).
106
+ - Pre-existing code outside the diff, unless the change actively makes it worse.
107
+ - Missing features or scope expansion - review the change, not the roadmap.
108
+ - Speculative hardening or "might need later" abstractions.
109
+
110
+ ## Output
111
+
112
+ Max 10 findings, highest impact first. Each: `[WARN]`/`[INFO]` `file:line - finding - evidence - rule cited - fix`. Use `[BLOCK]` for a core edit, a boundary break, or a §3c trace failure (a caller that no longer holds, an unfiltered query, a write outside its transaction). No prose around the list. Then one line per dimension you own (§ Focus): `DIMENSION: <name> - ran|n/a|partial - <counts, for n/a what you checked, for partial the files not reached>`, after any `PRIOR:` lines. End with **PASS** / **CHANGES REQUESTED** + one line on the most impactful finding.
@@ -0,0 +1,85 @@
1
+ ---
2
+ targets:
3
+ - '*'
4
+ name: security-reviewer
5
+ description: >-
6
+ Security review of changed overlay/frontend files for authz, secret/PII,
7
+ and input-validation risks in a real-money igaming consumer repo.
8
+ Findings only, no edits.
9
+ claudecode:
10
+ model: opus
11
+ ---
12
+
13
+ You are a security reviewer for a real-money igaming consumer repo built on `@openora/*`. Core money/auth logic lives upstream in the platform; you review what the OVERLAY adds: custom routes, adapter swaps, config, and the frontend. Findings only, no changes.
14
+
15
+ Stance: assume every protection in the diff is broken or bypassable until you trace the path that stops the attack - review to falsify, not to confirm the author's intent.
16
+
17
+ ## Grounding
18
+
19
+ If the orchestrator passed a base ref + changed-file list, use them - do not re-scope the diff. Otherwise: `git diff origin/{{mrTarget}}...HEAD --name-only`. Read each changed file, the immediate callees a finding depends on, and every caller `git grep -w` finds for a changed symbol or table. Prioritize overlay plugins/routes, adapter implementations (KYC, PSP, notifications), auth/session touchpoints, and anything reading env/secrets.
20
+
21
+ Reading map, at the main-checkout path the orchestrator passed (rule docs are rendered and gitignored, so never from a review worktree): `.claude/rules/oss-boundaries.md` and `docs/standards/errors.md`. No rule doc covers most security concerns - cite the named principle and the traced trigger instead (§6 of the `review` skill).
22
+
23
+ A UI-only diff still calls platform routes: open each route's guard in `@openora/core` (`adminGuard.assert(context, <resource>, <action>)` in the module router) and the resource-to-level map (`server/auth/permission-levels.ts`) to confirm a read-only role is refused on the server, not just hidden in the UI.
24
+
25
+ An `[oss]` file group (files in an OSS worktree under `{{ossDir}}/.worktrees/`) is core money/auth logic: review it against that worktree's `AGENTS.md`, `.rulesync/rules/*.md`, and `docs/standards/`, cite those, and prefix each finding `[oss]`.
26
+
27
+ ## Focus
28
+
29
+ `risk` (the default roster): also read `compliance-reviewer.md` next to this file IN FULL, follow its reading map, and run its checklist; you own `security` and `compliance` and return both `DIMENSION:` lines. `confirm` mode then needs zero hits in both domains.
30
+
31
+ ## Mode
32
+
33
+ - `confirm` (the precheck found no security keyword in the change, and `RISK:` is `low` or `medium`): within 5 tool calls, skim the reviewable files for anything the keyword list could miss - a route, a guard, a secret, input reaching a query or the DOM. Nothing: `DIMENSION: security - n/a - <what you checked>`. Something: `DIMENSION: security - escalate - <file>` and stop; the orchestrator re-runs you in `full` mode.
34
+ - `full`: the whole checklist below, starting from the `DOMAIN-HIT:` lines.
35
+
36
+ ## Budget and handoff
37
+
38
+ - Work within the tool-call budget the orchestrator passed. Batch: read several files or ranges in one shell call; open a callee only when a finding depends on it; never grep for what the `PRECHECK:` or `DOMAIN-HIT:` lines already state. Out of budget: stop and report the dimension as `partial` with the files you did not reach.
39
+ - `PRECHECK:` lines are facts about added lines, not findings: confirm each (it becomes a finding with the rule cited), downgrade it, or say in one line why it is a false positive.
40
+ - Never open a `SKIPPED:` file.
41
+ - Prior findings passed to you: re-verify each against the current code and return one `PRIOR: <finding> - fixed|still open|obsolete` line.
42
+
43
+ ## Request trace
44
+
45
+ Follow §3c of the `review` skill: walk the seven hops for each changed entry point, and check the blast radius: `git grep -w` each changed export, table symbol, and SQL table name across `*.ts`, `*.tsx`, `*.sql`, and open every caller found, not only the immediate callee; a caller that no longer holds is a `[BLOCK]`. Report one `TRACE:` line per entry point before the findings.
46
+
47
+ ## Checklist
48
+
49
+ ### Guards and intent
50
+
51
+ - [ ] For every guard the change adds or touches, ask how a hostile caller bypasses it: a spoofed or rotated header, a null or absent value that skips the check, an ordering that lets one caller drain another's budget.
52
+ - [ ] The change does what its title claims - a fix whose trigger still reproduces is a finding.
53
+
54
+ ### Authorization
55
+
56
+ - [ ] Overlay admin/backoffice routes enforce the platform guard - never a re-implemented role check.
57
+ - [ ] No client-supplied user id trusted for ownership decisions; caller resolved server-side.
58
+ - [ ] Frontend hides UI by role but the API is the enforcement point - flag authz that exists only client-side.
59
+
60
+ Outside focus `risk`, money paths, ledger integrity, and regulated gates belong to `compliance-reviewer` - do not duplicate them here.
61
+
62
+ ### Secrets & PII
63
+
64
+ - [ ] Vendor adapter credentials from env/config - never in source, templates, or client bundles.
65
+ - [ ] No PII (email, KYC docs, DOB, payment details) in logs, analytics events, error messages, or client-visible payloads.
66
+ - [ ] Nothing secret leaks into `NEXT_PUBLIC_*` or the browser bundle.
67
+
68
+ ### Input & injection
69
+
70
+ - [ ] All external input Zod-validated at the boundary (no `z.any()`/`z.unknown()` on a security edge).
71
+ - [ ] No raw SQL string interpolation; no inline `fetch` to vendors - adapters only (auditable egress).
72
+ - [ ] Webhooks from PSP/KYC vendors verify signatures before trusting payloads.
73
+ - [ ] Staff-supplied URLs rendered to players (`img src`, `href`, embeds) are validated as URLs with an allowed scheme and host - otherwise any host sees every player's IP, and a link can phish.
74
+ - [ ] A seed or fixture that deletes or overwrites rows refuses to run outside local and test environments.
75
+
76
+ ## Do NOT flag (false-positive guard)
77
+
78
+ - Attack paths you have not traced through the actual code - state the concrete trigger or don't raise it.
79
+ - Platform-core internals (upstream's responsibility) - flag only how the overlay USES them.
80
+ - Code outside the diff, unless the change makes it newly exploitable.
81
+ - Generic hardening wishlists (rate limits everywhere, CSP) with no tie to the changed surface.
82
+
83
+ ## Output
84
+
85
+ Max 10 findings, most severe first. Each: `[BLOCK]` (exploitable / data leak - file:line, risk, concrete fix) / `[WARN]` (missing defense-in-depth) / `[INFO]` (hardening). Then any `PRIOR:` lines, then exactly one line: `DIMENSION: security - ran|n/a|partial|escalate - <counts, for n/a what you checked, for partial the files not reached, for escalate the file>`. End with **PASS** / **CHANGES REQUESTED** + one line on the most severe finding.
@@ -0,0 +1,88 @@
1
+ ---
2
+ targets:
3
+ - '*'
4
+ name: tester
5
+ description: >-
6
+ Tester for a downstream igaming built on @openora/*. Drives a built change
7
+ against the operator's local stack, checks every test case and acceptance
8
+ criterion, attacks it like a player, a tester and a fraudster would, and
9
+ returns a per-case verdict table with video evidence. Adds the tests
10
+ its pass exercised; never fixes app code. Use as step 3 of ship, or to
11
+ verify a branch by hand.
12
+ claudecode:
13
+ model: sonnet
14
+ effort: high
15
+ ---
16
+
17
+ You verify a built change by hand against the running stack, debug failures with Chrome DevTools, and triage bugs - OSS core vs operator overlay. After the pass you add the tests it exercised, in the same branch. You start no subagents of your own: a domain doubt or a bug goes in your reply, and the orchestrator routes it.
18
+
19
+ ## Ground first
20
+
21
+ 1. `list-routes` (oss MCP) - full API surface (platform + operator routes).
22
+ 2. `catalog-overview` - active modules and expected behavior.
23
+ 3. `apps/api/src/extensions.config.ts` - active overlays and adapters.
24
+
25
+ ## Local stack
26
+
27
+ The platform is headless (API + modules); the player app and backoffice are this operator's own frontends. API :3001, player app :3000, backoffice :3002. Seed credentials (after `pnpm db:seed`): `admin@oss.dev` / `password1234`. Confirm ports and which UIs exist with the operator if they differ - an api-only consumer has no browser specs. If the stack isn't running, start what the cases need yourself (the API, plus the player app or backoffice for a browser case) per the repo's worktree rules, and stop it when you are done. Ports held by another worktree: never kill it - the cases that need it are UNCHECKED, saying so.
28
+
29
+ ## Your pass: verify by hand, then add the tests
30
+
31
+ A feature or fix PR carries its own tests (`docs/standards/testing.md`). Your deliverable is:
32
+
33
+ 1. List every test case from the plan you are handed and every acceptance criterion from the ticket (read per `docs/agents/issue-tracker.md`), numbered, before touching the app. An AC hidden in a comment or a linked page counts.
34
+ 2. Drive each AC against the running stack yourself, including the error states and the authz negatives. Assume the change is broken until the app proves otherwise - the developer's description and the videos already on the PR are claims, not evidence.
35
+ 3. **Try to break it** (below).
36
+ 4. Evidence per the section below.
37
+ 5. A **verdict table**: one row per case and AC - `case | PASS / FAIL / UNCHECKED | what happened, evidence (video, trace)`. Never PASS because a screen exists - the outcome the case names must be observed, and the row names where: the video path, the response, or the test that ran it. A browser case checked only through the API, or a case nothing ran, is UNCHECKED, saying why.
38
+ 6. The tests that pass exercised, committed in this branch (section below), and a **"Tests"** list: one line per test, tier (unit / API E2E / browser E2E) and path - it goes into the MR description.
39
+ 7. Clean up: drop the scratch data you made (rows, a database or Redis db you created for the pass, `apps/e2e/.auth` files you regenerated) and stop what you started. Say what you could not remove.
40
+
41
+ A broken flow is a FAIL row with the exact steps, expected vs actual and the file:line if you found it - the orchestrator sends it to `builder`. A failing spec you did not write: run it on the base commit too and name that run in the row; failing there as well is pre-existing, it is not this change's FAIL. Never call a spec pre-existing from reading code: unrun on the base, it is a FAIL.
42
+
43
+ ## Try to break it
44
+
45
+ After the happy path, attack the change the way a player, a bored tester and a fraudster would. Log every attempt with its result - a list of what you tried that held is part of the report. Only the attacks that fit the change: a copy fix needs none, a new money flow needs all of them.
46
+
47
+ - Input: empty, zero, negative, decimals past the currency's precision, the max and max+1, very long strings, unicode, pasted whitespace.
48
+ - Timing: double submit, rapid repeat clicks, refresh and browser back mid-flow, the same flow in two tabs, an expired session.
49
+ - Ownership: another player's ids in the URL or request body, a non-admin on an admin route, a logged-out call.
50
+ - State: a limit already spent, a self-excluded or unverified player, an insufficient balance, a bonus already active.
51
+ - Money paths: replay the request and check the ledger moved once.
52
+
53
+ ## Tests
54
+
55
+ They live in the change's own branch and follow the `conventions` tier: a route -> API E2E in `apps/e2e/tests/api/**` (happy + one hostile path); a screen -> browser spec; pure logic -> unit. An in-process test that mocks the database or a service is a defect - delete it and cover the behaviour with the API E2E.
56
+
57
+ E2E specs live in `apps/e2e/tests/<app>/<domain>/<scenario>.spec.ts` (Playwright projects `api`, `web` and `backoffice`) and follow the `e2e-conventions` rule: real seeded stack by default (that run blocks merge; `USE_MOCKS` only for a state you cannot produce for real), import `test`/`expect` from `fixtures.ts` (never `@playwright/test`), typed fixtures in `mocks/`, `data-testid` selectors, functional page objects. If `apps/e2e` is missing, scaffold it: `mkdir -p apps/e2e && cd apps/e2e && pnpm init && pnpm add -D @playwright/test && npx playwright install chromium`.
58
+
59
+ Debug and verify with the **Playwright CLI** first (`pnpm -F {{scope}}/e2e test <spec>`, `npx playwright screenshot <url> <file>`, or a throwaway spec with `page.screenshot`) - it costs a fraction of the tokens a browser MCP does. Reach for the **chrome-devtools** MCP only for a live console or network read the CLI cannot give you: navigate, reproduce the action, read console messages (JS errors, unhandled rejections), inspect network requests (status codes, response shapes), evaluate a script for DOM state, screenshot the failure.
60
+
61
+ **Evidence is mandatory.** Every pass ends with one short video per acceptance criterion (aim under 30 s, ending on the outcome the AC names; before and after on a fix), saved under `apps/e2e/test-results/evidence/<scenario>.webm`, and the file paths listed in the report so they reach the PR description - a human confirms the change by watching it, not by reading a description. Record with `playwright-cli` (`video-start`, `video-chapter "AC<n>: ..."`, drive it, `video-stop`) or a spec with `test.use({ video: 'on' })`. A screenshot only where a clip adds nothing, such as a second theme. API-only changes attach the request/response trace instead. Scale it to the change: a footer link needs one clip, a new money flow needs the whole journey.
62
+
63
+ ## Triage - the key question
64
+
65
+ Is the bug in OSS core or the operator's overlay?
66
+
67
+ - Fails in a fresh consumer scaffold / with no overlays active -> OSS core -> report upstream.
68
+ - Fails only with this operator's plugins/adapters -> a FAIL row for `builder`.
69
+ - Technically consistent but violates igaming rules -> a question for `expert` in your reply.
70
+
71
+ Severity: P0 blocks money/auth/game loop; P1 wrong business logic (wrong balance, bad geo-block); P2 broken UI / wrong API shape; P3 cosmetic (a row note, not a FAIL).
72
+
73
+ ## Core flows (priority order)
74
+
75
+ 1. Auth: register, login, logout, bad credentials.
76
+ 2. Wallet: balance, deposit, withdraw, history.
77
+ 3. Gaming: catalogue, start/end round, balance deduction.
78
+ 4. Bonus: claim, wagering progress.
79
+ 5. Compliance: deposit limits, geo-block.
80
+ 6. Backoffice: admin login, player list, KYC status.
81
+ 7. Each active operator overlay.
82
+
83
+ ## Rules
84
+
85
+ - Never modify platform or overlay code - a test that fails on app code is a FAIL row, not a fix.
86
+ - Assert on user-visible outcomes, not component internals.
87
+ - Commit only the tests you added, then run the repo's gate (`/check`) and the specs you touched once more on HEAD. Never push.
88
+ - Videos and screenshots stay under `apps/e2e/test-results/evidence/`, never committed; the orchestrator embeds them in the MR.
@@ -0,0 +1,26 @@
1
+ {
2
+ "vars": {
3
+ "name": "{{name}}",
4
+ "scope": "{{scope}}",
5
+ "trackerKey": "{{trackerKey}}",
6
+ "jiraSite": "{{jiraSite}}",
7
+ "jiraCloudId": "{{jiraCloudId}}",
8
+ "wikiSpace": "{{wikiSpace}}",
9
+ "teamChannel": "{{teamChannel}}",
10
+ "gitRemotePath": "{{gitRemotePath}}",
11
+ "ossDir": "{{ossDir}}",
12
+ "ossFromRoot": "{{ossFromRoot}}",
13
+ "mrTarget": "{{mrTarget}}"
14
+ },
15
+ "reviewPrecheck": {
16
+ "reactCompilerPaths": ["apps/web", "apps/backoffice"],
17
+ "extraSkipGlobs": [],
18
+ "domainPatterns": { "security": [], "compliance": [] }
19
+ },
20
+ "consumerOwned": [
21
+ ".rulesync/rules/overview.md",
22
+ ".rulesync/mcp.json",
23
+ ".rulesync/sync.json",
24
+ "docs/agents/forge.md"
25
+ ]
26
+ }
@@ -7,9 +7,8 @@
7
7
  "build": "tsc",
8
8
  "dev": "node --import tsx --watch --env-file-if-exists=../../.env src/main.ts",
9
9
  "start": "node --import tsx --env-file-if-exists=../../.env src/main.ts",
10
- "db:migrate": "node --import tsx --env-file-if-exists=../../.env src/migrate.ts",
11
10
  "db:seed": "node --import tsx --env-file-if-exists=../../.env src/seed.ts",
12
- "typecheck": "tsc --noEmit"
11
+ "check:types": "tsc --noEmit"
13
12
  },
14
13
  "dependencies": {
15
14
  "@openora/core": "{{coreVersion}}"
@@ -1,4 +1,5 @@
1
1
  import { corePlugins, type PluginEntry } from '@openora/core/server';
2
+ import { createRequire } from 'node:module';
2
3
  import { dirname, resolve } from 'node:path';
3
4
  import { fileURLToPath } from 'node:url';
4
5
 
@@ -7,16 +8,27 @@ import { fileURLToPath } from 'node:url';
7
8
  // later registration of a DI token wins, so an adapter override must come after
8
9
  // the module it replaces. Opt a core module out with a filter, eg
9
10
  // `corePlugins().filter((p) => p.id !== 'chat')`.
11
+ //
12
+ // Every `path` must be absolute: the plugin host imports it from inside
13
+ // @openora/core, where a relative path or a bare package name resolves from
14
+ // core's location instead of this file's.
10
15
 
11
16
  // Local overlays live alongside this file, under src/extensions/<name>/.
12
17
  const LOCAL = resolve(dirname(fileURLToPath(import.meta.url)), 'extensions');
13
18
 
19
+ // Add-on packages installed from a registry resolve from this app.
20
+ const fromApp = createRequire(import.meta.url).resolve;
21
+
14
22
  export const extensions: PluginEntry[] = [
15
23
  ...corePlugins(),
16
24
 
17
25
  // Your overlays. Generate with `pnpm gen plugin` / `pnpm gen adapter`.
18
26
  // Example (loaded after `wallet` so its PAYMENT_ADAPTER binding wins):
19
27
  // { id: 'my-payment', path: `${LOCAL}/my-payment/plugin.ts` },
28
+
29
+ // An add-on package, after the modules it depends on:
30
+ // { id: 'igaming-poker', path: fromApp('@acme/igaming-poker/plugin') },
20
31
  ];
21
32
 
22
33
  void LOCAL;
34
+ void fromApp;
@@ -67,18 +67,15 @@ process.env['EXTENSIONS_CONFIG'] ??= resolve(
67
67
  );
68
68
 
69
69
  async function bootstrap() {
70
- const { listen, emitOpenApiSpec } = await createApp({
70
+ const { listen } = await createApp({
71
71
  plugins: await loadExtensions(),
72
72
  contract,
73
73
  authSchema: { user, session, account, verification, twoFactor },
74
74
  igaming,
75
75
  port: Number(process.env['PORT'] ?? 3001),
76
- cors: { origins: process.env['CORS_ORIGINS']?.split(',') ?? '*' },
77
- openapi: { info: { title: '{{name}} API', version: '0.1.0' } },
78
76
  });
79
77
 
80
78
  await listen();
81
- await emitOpenApiSpec();
82
79
  }
83
80
 
84
81
  void bootstrap();
@@ -10,6 +10,9 @@
10
10
  */
11
11
  import { createDrizzleDb } from '@openora/core/server';
12
12
  import { seedRoles } from '@openora/core/iam/seed';
13
+ import { seedTag } from '@openora/core/pam/tag/seed';
14
+ import { seedAutoWithdrawalConfig } from '@openora/core/wallet/seed';
15
+ import { seedDefaultWeightProfile } from '@openora/core/promo/seed/bonus';
13
16
  // import additional module seeders here as you enable them
14
17
 
15
18
  async function main() {
@@ -27,7 +30,11 @@ async function main() {
27
30
  const db = createDrizzleDb(databaseUrl);
28
31
 
29
32
  await seedRoles(db);
30
- // await seedOtherModule(db);
33
+ await seedTag(db);
34
+ await seedAutoWithdrawalConfig(db);
35
+ // Without it, a bonus with no offer behind it - a gift, a rain drop, a hand-issued grant -
36
+ // resolves no weight profile and is refused.
37
+ await seedDefaultWeightProfile(db);
31
38
  console.log('Reference data seeded.');
32
39
  }
33
40
 
@@ -17,8 +17,20 @@ services:
17
17
 
18
18
  redis:
19
19
  image: redis:7-alpine
20
+ command:
21
+ [
22
+ 'redis-server',
23
+ '--maxmemory-policy',
24
+ 'noeviction',
25
+ '--appendonly',
26
+ 'yes',
27
+ '--appendfsync',
28
+ 'everysec',
29
+ ]
20
30
  ports:
21
31
  - '6379:6379'
32
+ volumes:
33
+ - redis_data:/data
22
34
  healthcheck:
23
35
  test: ['CMD', 'redis-cli', 'ping']
24
36
  interval: 5s
@@ -27,3 +39,4 @@ services:
27
39
 
28
40
  volumes:
29
41
  postgres_data:
42
+ redis_data:
@@ -0,0 +1,37 @@
1
+ # Working across this repo and the OSS checkout
2
+
3
+ Some changes need both repos: an OSS core change plus this repo's adaptation to it. Agents and humans do those the same way, from any agent tool.
4
+
5
+ ## Conventions
6
+
7
+ - **OSS code is changed only in a worktree**: `pnpm oss:worktree <branch>` creates or reuses `{{ossDir}}/.worktrees/<branch, / as +>` and installs its dependencies. The `guard-core` hook denies writes anywhere else in `{{ossDir}}` and in `node_modules`, in every tool rulesync renders hooks for. The main `{{ossDir}}` checkout stays on its branch: other sessions build against it.
8
+ - **Core gets generic changes only**: before touching core, state why it cannot live here and why another operator would want it. Operator-specific behavior stays here; core gets only the seam.
9
+ - **Any core change can be made here**, new features included, in the worktree. The `handoff` skill is for work that should run in a separate session rooted in the OSS repo.
10
+ - **Review before push**: `pnpm -C <worktree> verify`, then the OSS repo's own review workflow and its contract checklist on the worktree diff.
11
+ - **Same branch name in both repos** pairs the two requests. Skills find the OSS half from this repo's branch.
12
+ - **No links between the requests**: the shared branch name is the pairing. The OSS repo is public, so its PR never names the operator; this repo's request may be read by people outside the team, so it never names or links the OSS repo.
13
+ - **Run against the change**: `pnpm oss:worktree <branch> --link` builds the worktree and points `pnpm link:oss` at it; `pnpm link:oss` alone restores the main checkout. This repo runs core's built `dist/`, not its source, so after every core edit run `pnpm -C <worktree> build` before this repo or its e2e sees the change.
14
+ - **Review covers both halves**: the `review` skill finds the pair, reviews the OSS diff by the OSS repo's own rules, and cross-checks the contract between them.
15
+ - **Clean up** after both requests merge: `pnpm oss:worktree <branch> --remove`.
16
+
17
+ The full procedure lives in "Changing OSS core" in the `oss-boundaries` rule.
18
+
19
+ ## Example asks
20
+
21
+ One session covers both halves; start it here with `{{ossDir}}` added (see below).
22
+
23
+ - "{{trackerKey}}-123: the withdrawal list needs a `currency` filter. Add it to the OSS contract in a worktree, then use it here."
24
+ - "The OSS bug behind {{trackerKey}}-456: fix it in a worktree, link it, and rerun this repo's e2e against it."
25
+ - "Review this branch and its OSS pair."
26
+ - "Both requests merged: bump `@openora/*` and remove the worktree."
27
+
28
+ ## Giving each tool access to `{{ossDir}}`
29
+
30
+ Grant the whole `{{ossDir}}`, not only `.worktrees/`: a worktree keeps its git metadata in `{{ossDir}}/.git`, so committing there writes to it. The hook still limits file edits to the worktree.
31
+
32
+ - **Claude Code**: `permissions.additionalDirectories: ["{{ossDir}}"]` in `.claude/settings.json`, checked in. Takes effect once the workspace is trusted. That grants file access only; `claude --add-dir {{ossDir}}` (or `/add-dir {{ossDir}}`) also loads the OSS repo's skills and agents, and with `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1` in the `env` block of `.claude/settings.json`, checked in, its `CLAUDE.md` and `.claude/rules/`.
33
+ - **Codex CLI**: `codex --add-dir {{ossDir}}`. `.codex/config.toml` is generated by rulesync, so a checked-in `writable_roots` entry would be overwritten. A personal alternative is `[sandbox_workspace_write] writable_roots` in `~/.codex/config.toml` with an absolute path.
34
+ - **GitHub Copilot CLI**: `copilot --add-dir {{ossDir}}`, or `/add-dir {{ossDir}}` in the session. No project-level key exists.
35
+ - **Gemini CLI**: `gemini --include-directories {{ossDir}}`, or `/directory add {{ossDir}}`. `.gemini/settings.json` is generated by rulesync.
36
+
37
+ Each tool reads only this repo's instruction files at start. The rules therefore tell every agent to read the OSS repo's `AGENTS.md`, `.rulesync/rules/`, and `docs/standards/` from the worktree before editing or reviewing there, so no tool depends on loading a second repo's instructions.
@@ -0,0 +1,54 @@
1
+ # Code forge: GitHub
2
+
3
+ Code review for `{{gitRemotePath}}` happens on GitHub pull requests, driven by the [`gh`](https://cli.github.com) CLI. Every skill that opens, reads, or comments on a pull request reads this file instead of hardcoding commands, so a repo on a different forge rewrites this one file and the skills keep working.
4
+
5
+ Swapping forge: keep the headings below and replace the commands. For GitLab that is `glab`, "merge request" for "pull request", `glab mr create/view/diff/note`, and `glab api "projects/<path-with-%2F>/merge_requests/<n>/discussions"` with a `position` object for inline comments.
6
+
7
+ ## Coordinates
8
+
9
+ - Repo: `{{gitRemotePath}}`. CLI: `gh` (authenticate once with `gh auth login`).
10
+ - Noun: pull request, "PR" for short. The default target branch is `{{mrTarget}}`.
11
+
12
+ ## Where a change lands
13
+
14
+ - Topic branch (`feat/*`, `fix/*`, `{{trackerKey}}-*`) -> `{{mrTarget}}`.
15
+ - A repo that promotes through environment branches adds them here (for example `{{mrTarget}}` -> `stage` -> `prod`) and never opens a PR straight from a topic branch to the last one.
16
+ - If the current branch is not listed, target `{{mrTarget}}`.
17
+
18
+ ## Open a pull request
19
+
20
+ ```
21
+ gh pr create --base <target> --head <current> --title "<type>: <summary>" --body "<body>"
22
+ ```
23
+
24
+ Reuse an open PR for the same head -> base instead of opening a duplicate: `gh pr list --head <current> --base <target>`. Never delete a long-lived branch on merge.
25
+
26
+ ## Read a pull request
27
+
28
+ - Intent and discussion: `gh pr view <n> --comments`
29
+ - Patch: `gh pr diff <n>`
30
+ - Review comments (resolved and unresolved - this endpoint has no resolved state): `gh api --paginate "repos/{{gitRemotePath}}/pulls/<n>/comments"`
31
+ - CI: `gh pr checks <n>`; wait for it to finish with `gh pr checks <n> --watch` (GitLab: `glab ci status --live`)
32
+ - Source to review, without switching the working tree: `git fetch origin "$(gh pr view <n> --json headRefName -q .headRefName)" && git worktree add --detach .claude/worktrees/review-<n> "$(gh pr view <n> --json headRefOid -q .headRefOid)"`
33
+ - Last review marker (`review` skill §2a): `gh api --paginate "repos/{{gitRemotePath}}/issues/<n>/comments" --jq '[.[] | select(.body | contains("<!-- review:sha=")) | select(.author_association == "OWNER" or .author_association == "MEMBER" or .author_association == "COLLABORATOR") | select(.user.login != "<pr-author>")] | last | .body' | grep -o 'review:sha=[0-9a-f]*' | tail -1`, with `<pr-author>` from `gh pr view <n> --json author -q .author.login`. Both filters are load-bearing: anyone can post a comment containing the marker text for any SHA they like, so an outside contributor's marker and the PR author's own (a member can be both) are never trusted as a completed review. The marker only narrows an interactive review; `--ci` ignores it.
34
+
35
+ ## Attach evidence
36
+
37
+ `gh` has no command to upload an image. Open the PR in the browser and drag each file into the description or a comment; GitHub inserts the embed markdown, keep it. If a file cannot be uploaded, say in the description that it is available from the author. Never paste a local path.
38
+
39
+ ## Inline review comments
40
+
41
+ Anchor each comment to a line of the diff on the head commit:
42
+
43
+ ```
44
+ gh api "repos/{{gitRemotePath}}/pulls/<n>/comments" \
45
+ -f body="<comment>" -f commit_id="<head-sha>" -f path="<file>" -F line=<new-line> -f side=RIGHT
46
+ ```
47
+
48
+ Take `<head-sha>` from `gh pr view <n> --json headRefOid`. Anchor on the NEW-file line of an added line. Verify the response carries a non-null `line`; if it came back as a plain issue comment, delete it and retry rather than leaving an unanchored note. Post the verdict itself as one summary comment: `gh pr comment <n> --body "<one sentence>"`.
49
+
50
+ ## Rules
51
+
52
+ - Never push without an explicit per-action confirmation from the user.
53
+ - Never merge a pull request unless the user asks for that exact action.
54
+ - Never resolve someone else's review thread.
@@ -0,0 +1,42 @@
1
+ # Issue tracker: Jira (project `{{trackerKey}}`) + Confluence + Slack
2
+
3
+ Work for this operator is tracked in Jira project `{{trackerKey}}` on `{{jiraSite}}`. Requirements and reasoning live in Confluence space `{{wikiSpace}}`. Decisions that never made it into a ticket live in Slack `{{teamChannel}}`. Code review happens on `{{gitRemotePath}}` - see `docs/agents/forge.md`.
4
+
5
+ Every skill that says "fetch the relevant ticket" reads this file.
6
+
7
+ ## Keys and where they appear
8
+
9
+ - Ticket key: `{{trackerKey}}-<n>`. `{{trackerKey}}-0` means "no ticket" (chores).
10
+ - Branch: `<type>/{{trackerKey}}-<n>/<slug>`. MR title: conventional commit subject; MR description ends with `Closes {{trackerKey}}-<n>` when it finishes the ticket, `Refs {{trackerKey}}-<n>` when it is one of several MRs for it. If your forge's tracker integration keys on `Closes`, `Refs` never moves the ticket.
11
+ - Resolve the key from, in order: the argument the user gave, the MR description, the MR title, the branch name, commit subjects.
12
+
13
+ ## What "read the ticket" means
14
+
15
+ A ticket is read when ALL of this has been seen - never from the description alone:
16
+
17
+ 1. Description and acceptance criteria (task items render as `- [ ]` / `- [x]`).
18
+ 2. Every comment, with author and date - decisions and scope changes hide there.
19
+ 3. Every attachment and every inline image, downloaded and viewed as pixels. Filenames, thumbnails, `blob:` links and metadata prove nothing.
20
+ 4. Parent epic and linked issues named by the AC.
21
+ 5. Every Confluence page the ticket links - its body, its images, its inline and footer comments. The AC are the spec; the page behind them is the reasoning.
22
+ 6. A Slack thread, only when the ticket or MR says the design or decision lives there ("shared in chat", a Slack link). Optional otherwise.
23
+
24
+ How:
25
+
26
+ - With the `atlassian-read` skill installed: `python3 ~/.claude/skills/atlassian-read/scripts/read.py issue {{trackerKey}}-<n>` then `page <id>` for each linked page. It downloads everything and prints a manifest mapping each image to the comment it came from.
27
+ - Without it, REST with an Atlassian API token: Jira `GET /rest/api/3/issue/{{trackerKey}}-<n>?fields=summary,status,description,attachment,comment,issuelinks,parent`, attachments `GET /rest/api/3/attachment/content/<id>`; Confluence `GET /wiki/api/v2/pages/<id>?body-format=atlas_doc_format`, `/attachments`, `/footer-comments`, `/inline-comments`, download `/wiki<downloadLink>`. Bodies are ADF: walk `text` nodes for prose and `media`/`mediaInline` nodes for images (`attrs.alt` is the Jira attachment filename; on Confluence `attrs.id` is the attachment `fileId`).
28
+ - The Atlassian MCP is fine for search, text and writes, but it returns no image bytes - it is never sufficient on its own when attachments exist.
29
+ - Slack: the `slack-reader` agent when available, else the Slack MCP, scoped to the channel and thread named.
30
+
31
+ Credentials are personal: `CONFLUENCE_EMAIL` / `CONFLUENCE_TOKEN` (an Atlassian API token) from a local, untracked env file. Never commit them, never echo them, never store curl flags holding them in a shell variable.
32
+
33
+ ## When a skill says "fetch the relevant ticket"
34
+
35
+ Run the protocol above and distill: goal in one line, AC quoted as bullets, decisions from comments (who, when), design references (which screenshot shows what), out-of-scope lines. Report "no ticket" when no key resolves and "no access" when a fetch fails - never fall back silently, never invent AC.
36
+
37
+ ## Writing
38
+
39
+ - Never comment on, transition, or edit a Jira issue or Confluence page unless the user asks for that exact write. Reading authorizes nothing.
40
+ - The one standing exception: `/ship {{trackerKey}}-<n>` is the ask for its own writes on that ticket - the Plan, questions, blocker and MR-link comments, the test cases where the `test-plan` skill keeps them, and the transitions to In Progress and Code Review. Nothing else.
41
+ - Review comments go through `review --post`, draft-and-confirm, using the commands in `docs/agents/forge.md`.
42
+ - Slack: draft only, never send.
@@ -0,0 +1,16 @@
1
+ # Comments
2
+
3
+ Read this before writing a comment or JSDoc.
4
+
5
+ - Comment WHY, never WHAT: hidden constraint, invariant, bug workaround (link it), trade-off. If a
6
+ block needs a comment to be understood, rename/extract instead.
7
+ - No section-divider comments (`// ---`, `// ===`).
8
+ - Every JSDoc is multi-line, always - `/**`, the text, and `*/` each on their own line. A
9
+ single-line `/** ... */` is never allowed, even for one sentence. Add one only on an independent
10
+ function/class (not a React component or hook) that is >~15 lines or has non-obvious params. One
11
+ sentence; document the surprising contract, not the name.
12
+ - Never JSDoc a React component or hook - a genuinely surprising note goes on the specific
13
+ prop/param type, or an inline comment at the call site.
14
+ - Deferred work: `// TODO:` with the concrete follow-up, never a bare TODO.
15
+ - Placeholder/sample data and stubs: greppable `// mock:` comment so throwaway code stays
16
+ findable.