create-mercato-app 0.6.5-develop.4639.1.0416d895fa → 0.6.5-develop.4656.1.5b0d4fd8a6
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.
- package/agentic/shared/AGENTS.md.template +23 -0
- package/agentic/shared/ai/skills/om-code-review/references/review-checklist.md +3 -0
- package/agentic/shared/ai/skills/om-data-model-design/SKILL.md +2 -0
- package/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +7 -8
- package/agentic/shared/ai/skills/om-prepare-issue/SKILL.md +202 -0
- package/agentic/shared/ai/skills/om-spec-writing/references/spec-checklist.md +1 -1
- package/dist/agentic/guides/core.customers.md +1 -1
- package/dist/agentic/shared/AGENTS.md.template +23 -0
- package/dist/agentic/shared/ai/skills/om-code-review/references/review-checklist.md +3 -0
- package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +2 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +7 -8
- package/dist/agentic/shared/ai/skills/om-prepare-issue/SKILL.md +202 -0
- package/dist/agentic/shared/ai/skills/om-spec-writing/references/spec-checklist.md +1 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/template/AGENTS.md +22 -0
|
@@ -154,12 +154,35 @@ When the user asks to **create a new application** or a **new module**, do not i
|
|
|
154
154
|
| **Multi-tenant scoping (default for every entity)** | Every tenant-scoped entity MUST include indexed `organization_id` and `tenant_id` columns and every read/write MUST filter by them. The CRUD factory injects scope automatically — do NOT bypass it. For ad-hoc queries use `withScopedPayload` from `@open-mercato/shared/lib/api/scoped` | `.ai/skills/om-data-model-design/SKILL.md`; <https://docs.open-mercato.dev/architecture/system-overview> |
|
|
155
155
|
| **Encryption maps for sensitive data** | Declare a module-level `<module>/encryption.ts` exporting `defaultEncryptionMaps: ModuleEncryptionMap[]` from `@open-mercato/shared/modules/encryption`. Read encrypted columns via `findWithDecryption` / `findOneWithDecryption` from `@open-mercato/shared/lib/encryption/find`. NEVER hand-roll AES/KMS, NEVER use `em.find` on encrypted columns | `.ai/skills/om-data-model-design/SKILL.md` → Sensitive Data and Encryption Maps; <https://docs.open-mercato.dev/user-guide/encryption> |
|
|
156
156
|
| **Cache** | Resolve the cache from DI (`container.resolve('cache')`) — never `new Redis(...)` or raw SQLite. Tag with `tenant:<id>` / `org:<id>` and the entity-type tag so invalidation stays tenant-scoped | `.ai/guides/cache.md`; <https://docs.open-mercato.dev/user-guide/cache-management> |
|
|
157
|
+
| **Entity update safety** | Multi-phase scalar + relation mutations use `withAtomicFlush(em, phases, { transaction: true })` from `@open-mercato/shared/lib/commands/flush` — never interleave `em.find`/`em.findOne` between a scalar mutation and `em.flush()`. Keep `emitCrudSideEffects` + cache invalidation OUTSIDE it (they fire after commit) | See **Entity Update Safety** section below |
|
|
157
158
|
| **Background workers** | `src/modules/<id>/workers/*.ts` exporting `metadata: { queue, id?, concurrency? }` + default handler. Never spin up custom queues | `.ai/guides/queue.md`; <https://docs.open-mercato.dev/framework/events/queue-workers> |
|
|
158
159
|
| **Events between modules** | `<module>/events.ts` with `createModuleEvents({ moduleId, events } as const)`. Subscribers in `subscribers/*.ts`. Never call other modules' services directly across module boundaries | `.ai/guides/events.md`; <https://docs.open-mercato.dev/framework/events/overview> |
|
|
159
160
|
| **i18n (every user-facing string)** | `useT()` client-side from `@open-mercato/shared/lib/i18n/context`, `resolveTranslations()` server-side from `@open-mercato/shared/lib/i18n/server`; keys in `src/i18n/<locale>.json`. Never hard-code labels in components | `.ai/guides/shared.md` → i18n |
|
|
160
161
|
|
|
161
162
|
> Rule of thumb: if you find yourself reaching for raw `fetch`, raw `<form>`, ad-hoc `crypto`, ad-hoc `Redis`, or a manual `m2m` join across modules, stop and check the row above — there is a canonical helper.
|
|
162
163
|
|
|
164
|
+
## Entity Update Safety
|
|
165
|
+
|
|
166
|
+
MikroORM v7 can silently discard pending scalar changes when a query (`em.find`, `em.findOne`, sync helper) runs on the same `EntityManager` between a scalar mutation and `em.flush()`. For any command in `{{PROJECT_NAME}}` that mutates entities across multiple phases:
|
|
167
|
+
|
|
168
|
+
- MUST use `withAtomicFlush(em, phases, { transaction: true })` from `@open-mercato/shared/lib/commands/flush`.
|
|
169
|
+
- NEVER interleave `em.find`/`em.findOne`/sync helpers between a scalar mutation and `em.flush()` on the same `EntityManager` without `withAtomicFlush` — the UPDATE is dropped.
|
|
170
|
+
- Keep `emitCrudSideEffects` (and `emitCrudUndoSideEffects`) AND cache invalidation OUTSIDE the `withAtomicFlush` block — they fire only AFTER the DB write commits.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
174
|
+
|
|
175
|
+
await withAtomicFlush(em, [
|
|
176
|
+
() => { record.name = 'New Name'; record.status = 'active' },
|
|
177
|
+
() => syncEntityTags(em, record, tags),
|
|
178
|
+
], { transaction: true, label: '<module>.<command>' })
|
|
179
|
+
|
|
180
|
+
// Side effects + cache invalidation AFTER the atomic flush (post-commit)
|
|
181
|
+
await emitCrudSideEffects({ /* ... */ })
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Because invalidation runs post-commit and the query-index read-projection tail (search tokens, vectors, fulltext, coverage) converges asynchronously, reads can briefly see a convergence window after a write. An opt-in env flag, `OM_CACHE_SAFETY_ALWAYS_CONSISTENT` (default OFF, backward compatible), is planned to make that tail converge synchronously on write at a write-latency cost — treat it as opt-in/forthcoming, not on by default.
|
|
185
|
+
|
|
163
186
|
## CRITICAL rules — always follow without exception
|
|
164
187
|
|
|
165
188
|
1. **Entity classes live in `src/modules/<module>/data/entities.ts` and MUST import decorators from `@mikro-orm/decorators/legacy`.** Start there for every schema change.
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
- [ ] UUID primary keys with standard columns (`id`, `created_at`, `updated_at`)
|
|
26
26
|
- [ ] Soft delete via `deleted_at` where applicable
|
|
27
27
|
- [ ] Atomic transactions for multi-step writes
|
|
28
|
+
- [ ] `withAtomicFlush(em, phases, { transaction: true })` used when mutating across phases that include queries on the same `EntityManager`
|
|
29
|
+
- [ ] No `em.find`/`em.findOne`/sync helpers between a scalar mutation and `em.flush()` without `withAtomicFlush`
|
|
28
30
|
|
|
29
31
|
## 4. API Routes
|
|
30
32
|
|
|
@@ -42,6 +44,7 @@
|
|
|
42
44
|
- [ ] Workers export `metadata` with `{ queue, id?, concurrency? }`
|
|
43
45
|
- [ ] All mutations implemented as commands with undo logic
|
|
44
46
|
- [ ] Side effects outside `withAtomicFlush`
|
|
47
|
+
- [ ] Cache invalidation and side effects (`emitCrudSideEffects`) fire AFTER the DB write commits — never inside the `withAtomicFlush` block
|
|
45
48
|
|
|
46
49
|
## 6. UI & Backend Pages
|
|
47
50
|
|
|
@@ -443,6 +443,8 @@ const items = await em.find(Entity, {
|
|
|
443
443
|
})
|
|
444
444
|
```
|
|
445
445
|
|
|
446
|
+
> **Multi-phase or relation-syncing writes:** the bare `em.flush()` above is fine for a single scalar update. As soon as a write mutates across multiple phases or runs a query (`em.find`/`em.findOne`/sync helper) between a scalar mutation and the flush, switch to `withAtomicFlush(em, phases, { transaction: true })` from `@open-mercato/shared/lib/commands/flush` — MikroORM v7 silently drops the scalar UPDATE otherwise. Never query between scalar mutations and flush; keep side effects + cache invalidation outside the flush (after commit).
|
|
447
|
+
|
|
446
448
|
### Audit/History Table
|
|
447
449
|
|
|
448
450
|
For tracking changes to an entity:
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
| Module ID | plural, snake_case | `fleet_vehicles`, `loyalty_points` |
|
|
8
8
|
| Module folder | same as module ID | `src/modules/fleet_vehicles/` |
|
|
9
9
|
| Entity class | PascalCase, singular | `FleetVehicle`, `LoyaltyPoint` |
|
|
10
|
-
| Entity file |
|
|
10
|
+
| Entity file | single `data/entities.ts` (one class per entity) | `data/entities.ts` → `class FleetVehicle` |
|
|
11
11
|
| Table name | plural, snake_case | `fleet_vehicles`, `loyalty_points` |
|
|
12
12
|
| Column name | snake_case | `vehicle_type`, `point_balance` |
|
|
13
13
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|---------|-----------|---------|
|
|
18
18
|
| JS/TS fields | camelCase | `vehicleType`, `pointBalance` |
|
|
19
19
|
| Event ID | `module.entity.action` (dots, singular entity, past tense) | `fleet_vehicles.vehicle.created` |
|
|
20
|
-
| Feature ID | `module.action` | `fleet_vehicles.view`, `fleet_vehicles.
|
|
20
|
+
| Feature ID | `module.entity.action` (per-entity; use `view` / `manage`) | `fleet_vehicles.vehicle.view`, `fleet_vehicles.vehicle.manage` |
|
|
21
21
|
| Enricher ID | `module.enricher-name` | `fleet_vehicles.maintenance-stats` |
|
|
22
22
|
| Widget ID | `module.injection.widget-name` | `fleet_vehicles.injection.status-column` |
|
|
23
23
|
| Interceptor ID | `module.interceptor-name` | `fleet_vehicles.validate-vin` |
|
|
@@ -39,12 +39,11 @@ deleted_at: Date | null // Soft delete timestamp
|
|
|
39
39
|
|
|
40
40
|
## API Routes
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
|
45
|
-
|
|
46
|
-
|
|
|
47
|
-
| DELETE | `api/delete/<entities>.ts` | `DELETE /api/<module>/<entities>` |
|
|
42
|
+
All HTTP methods live in a **single** `api/<entities>/route.ts` that exports named handlers `{ GET, POST, PUT, DELETE }` + `metadata` + `openApi` (not separate `api/get/`, `api/post/` files).
|
|
43
|
+
|
|
44
|
+
| File Path | Methods | URL |
|
|
45
|
+
|-----------|---------|-----|
|
|
46
|
+
| `api/<entities>/route.ts` | `GET` / `POST` / `PUT` / `DELETE` | `/api/<module>/<entities>` |
|
|
48
47
|
|
|
49
48
|
## Backend Pages
|
|
50
49
|
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: om-prepare-issue
|
|
3
|
+
description: Capture a feature the user wants built later without building it now. Researches and writes a spec via the om-spec-writing conventions, ships it as a docs-only spec PR against `develop` (reusing om-auto-create-pr mechanics; `skip-qa`, `documentation`), then opens a tracking GitHub issue that links the spec path and the spec PR so the work can be picked up later with om-auto-fix-github or om-implement-spec. Use for "park this idea", "write a spec and an issue for later", "prepare an issue to build X eventually", "spec it out but don't implement yet".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Prepare Issue (deferred work)
|
|
7
|
+
|
|
8
|
+
Turn a "we want this eventually" brief into durable, actionable backlog without implementing it. The deliverable is three linked artifacts:
|
|
9
|
+
|
|
10
|
+
1. A **spec** under `.ai/specs/` written to `om-spec-writing` standards.
|
|
11
|
+
2. A **docs-only spec PR** against `develop` that adds only that spec file (`documentation`, `skip-qa`).
|
|
12
|
+
3. A **GitHub issue** to implement the spec, linking both the spec path and the spec PR.
|
|
13
|
+
|
|
14
|
+
This skill is for **deferred** work. It does NOT implement the feature. If the user wants the feature built now, hand off to `om-auto-create-pr` (free-form task) or `om-implement-spec` (after the spec exists) instead.
|
|
15
|
+
|
|
16
|
+
This skill reuses the worktree/branch/commit/label discipline of `.ai/skills/om-auto-create-pr/SKILL.md` for the PR, the spec methodology of `.ai/skills/om-spec-writing/SKILL.md` for the spec, and the issue-claim/linking conventions of `.ai/skills/om-auto-fix-github/SKILL.md` for the tracking issue. Read those before deviating.
|
|
17
|
+
|
|
18
|
+
## Arguments
|
|
19
|
+
|
|
20
|
+
- `{brief}` (required) — free-form description of the feature to capture. One sentence or several paragraphs.
|
|
21
|
+
- `--slug <kebab-case>` (optional) — override the slug used in the spec filename and branch. Default: derived from the brief.
|
|
22
|
+
- `--enterprise` (optional) — write the spec under `.ai/specs/enterprise/` instead of `.ai/specs/` (commercial scope). Default: OSS scope (`.ai/specs/`).
|
|
23
|
+
- `--priority <low|medium|high|extreme>` (optional) — priority label for the tracking issue. Default: unset (treated as `priority-medium`).
|
|
24
|
+
- `--no-issue` (optional) — write the spec and open the spec PR, but skip issue creation. Use when the user only wants the spec on record.
|
|
25
|
+
- `--force` (optional) — bypass the claim-conflict check when a previous run left a branch or spec file behind.
|
|
26
|
+
|
|
27
|
+
## Workflow
|
|
28
|
+
|
|
29
|
+
### 0. Pre-flight and claim
|
|
30
|
+
|
|
31
|
+
Follow `.ai/skills/om-auto-create-pr/SKILL.md` step 0 verbatim. This is new docs work, so the branch MUST use the `feat/` prefix.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
CURRENT_USER=$(gh api user --jq '.login')
|
|
35
|
+
DATE=$(date -u +%Y-%m-%d)
|
|
36
|
+
SLUG="{slug-or-derived}"
|
|
37
|
+
SPEC_DIR=".ai/specs" # or .ai/specs/enterprise when --enterprise
|
|
38
|
+
SPEC_PATH="${SPEC_DIR}/${DATE}-${SLUG}.md"
|
|
39
|
+
BRANCH="feat/prepare-${SLUG}"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A run is **already in progress** when ANY of these is true (treat as re-entry if the current user owns it, otherwise STOP and ask via `AskUserQuestion` unless `--force`):
|
|
43
|
+
|
|
44
|
+
- A file at `$SPEC_PATH` already exists on `origin/develop` or any remote branch.
|
|
45
|
+
- A remote branch `origin/${BRANCH}` already exists.
|
|
46
|
+
- An open PR already adds `$SPEC_PATH`.
|
|
47
|
+
- An open issue already tracks the same feature (search before creating — see step 5).
|
|
48
|
+
|
|
49
|
+
### 1. Triage the brief before writing
|
|
50
|
+
|
|
51
|
+
Read enough context to write a credible spec, not to build it:
|
|
52
|
+
|
|
53
|
+
- Match the brief to rows in the root `AGENTS.md` Task Router and read every matching guide (module, UI, search, events, etc.).
|
|
54
|
+
- Check `.ai/specs/` and `.ai/specs/enterprise/` for an existing spec covering the same area. If one exists, extend or supersede it instead of duplicating — confirm the direction with the user via `AskUserQuestion`.
|
|
55
|
+
- Skim `.ai/lessons.md`.
|
|
56
|
+
|
|
57
|
+
Reduce the brief to: goal in one sentence, affected modules/packages, and the rough scope. If a wrong assumption would force a spec rewrite, surface it as an Open Question (see step 2) rather than guessing.
|
|
58
|
+
|
|
59
|
+
### 2. Write the spec with om-spec-writing
|
|
60
|
+
|
|
61
|
+
Follow `.ai/skills/om-spec-writing/SKILL.md` end to end. Key points for this skill:
|
|
62
|
+
|
|
63
|
+
- Create the spec at `$SPEC_PATH` (`{YYYY-MM-DD}-{kebab-title}.md`, `date` UTC). Enterprise scope goes under `.ai/specs/enterprise/`.
|
|
64
|
+
- Start with a **Skeleton Spec** (TLDR + 2-3 key sections). If critical unknowns exist, add a numbered **Open Questions** block right after the TLDR and **STOP** — ask the user before filling in the rest. This is a hard gate; do not invent answers to architecture-blocking questions.
|
|
65
|
+
- After answers land, expand the spec: Problem Statement, Proposed Solution, Phasing (stories), Implementation Plan (testable Steps), and the architectural concerns the `om-spec-writing` lens requires (singular naming, FK-only cross-module links, `organization_id` scoping, undoability, zod validation, encryption maps for sensitive columns, canonical primitives, Design System tokens, Frontend Architecture Contract when UI is touched).
|
|
66
|
+
- The spec MUST include an **integration coverage** section listing the affected API paths and key UI paths the implementer will need to test (root `AGENTS.md` → Documentation and Specifications requires this for every feature).
|
|
67
|
+
- Apply the `om-spec-writing` Spec Checklist and Final Compliance Review before finalizing.
|
|
68
|
+
|
|
69
|
+
Do NOT write any implementation code, migrations, or module files. The only file this run adds is the spec.
|
|
70
|
+
|
|
71
|
+
### 3. Isolated worktree, branch, and first commit
|
|
72
|
+
|
|
73
|
+
Follow `.ai/skills/om-auto-create-pr/SKILL.md` steps 4–5 verbatim. Base is always `develop`. Commit the spec as the first commit, then push.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git add "$SPEC_PATH"
|
|
77
|
+
git commit -m "docs(spec): add ${SLUG} spec for deferred implementation"
|
|
78
|
+
git push -u origin "$BRANCH"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
If the Open Questions gate in step 2 is still unresolved, do not create the worktree or push — resolve the questions with the user first.
|
|
82
|
+
|
|
83
|
+
### 4. Open the spec PR
|
|
84
|
+
|
|
85
|
+
This is a docs-only / spec-only PR. The minimum validation gate is the docs-only gate from `.ai/skills/om-auto-create-pr/SKILL.md` step 7:
|
|
86
|
+
|
|
87
|
+
- `yarn lint` if it catches markdown/YAML issues.
|
|
88
|
+
- `git diff --check` — no trailing whitespace, no merge markers.
|
|
89
|
+
- A manual re-read of the spec.
|
|
90
|
+
|
|
91
|
+
Never run the full code gate (`yarn test` / `yarn typecheck` / `yarn build:app`) for a spec-only run.
|
|
92
|
+
|
|
93
|
+
Open the PR against `develop`:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
gh pr create --base develop \
|
|
97
|
+
--title "docs(spec): ${SLUG} — deferred implementation spec" \
|
|
98
|
+
--body-file <(cat <<'EOF'
|
|
99
|
+
## Goal
|
|
100
|
+
- {one-line feature summary from the brief}
|
|
101
|
+
|
|
102
|
+
## What this PR adds
|
|
103
|
+
- Spec only: `{SPEC_PATH}`. No implementation, no code, no migrations.
|
|
104
|
+
|
|
105
|
+
## Why now
|
|
106
|
+
- Captures deferred work as a reviewable spec so it can be implemented later via `om-implement-spec` / `om-auto-fix-github`.
|
|
107
|
+
|
|
108
|
+
## Tracking issue
|
|
109
|
+
- {issue URL — filled in after step 5, or "see follow-up comment"}
|
|
110
|
+
|
|
111
|
+
## Backward Compatibility
|
|
112
|
+
- No contract surface changes (spec document only).
|
|
113
|
+
EOF
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Apply labels per root `AGENTS.md` PR workflow, each followed by a short explanatory comment (per the `om-auto-create-pr` label-comment rule):
|
|
118
|
+
|
|
119
|
+
- `review` — "PR is ready for code review."
|
|
120
|
+
- `documentation` — "spec-only deliverable under `.ai/specs/`."
|
|
121
|
+
- `skip-qa` — "spec/docs-only; no customer-facing runtime behavior."
|
|
122
|
+
|
|
123
|
+
Never add `needs-qa` to a prepare-issue PR — it adds no runtime behavior to exercise.
|
|
124
|
+
|
|
125
|
+
### 5. Create the tracking issue
|
|
126
|
+
|
|
127
|
+
Skip this step only when `--no-issue` was passed.
|
|
128
|
+
|
|
129
|
+
First, avoid duplicates — search for an existing tracking issue:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
gh issue list --state open --search "${SLUG} in:title,body" --json number,title,url
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If a credible duplicate exists, link the spec/PR in a comment on that issue instead of opening a new one, and report which issue you reused.
|
|
136
|
+
|
|
137
|
+
Otherwise create the issue. It MUST link the spec path and the spec PR, and name the recommended implementer skill so a future run can pick it up:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
gh issue create \
|
|
141
|
+
--title "Implement: {feature title}" \
|
|
142
|
+
--label "feature" \
|
|
143
|
+
--body-file <(cat <<'EOF'
|
|
144
|
+
## Summary
|
|
145
|
+
- {one-line feature summary from the brief}
|
|
146
|
+
|
|
147
|
+
## Spec
|
|
148
|
+
- Implementation spec: `{SPEC_PATH}`
|
|
149
|
+
- Spec PR: {spec PR URL}
|
|
150
|
+
|
|
151
|
+
## How to implement
|
|
152
|
+
- Once the spec PR merges, run `/om-implement-spec {SPEC_PATH}` (multi-phase) or `/om-auto-fix-github {thisIssueNumber}` (smaller scope).
|
|
153
|
+
- Do not start implementation until the spec PR is merged into `develop`.
|
|
154
|
+
|
|
155
|
+
## Scope notes
|
|
156
|
+
- {affected modules/packages}
|
|
157
|
+
- {non-goals captured in the spec}
|
|
158
|
+
EOF
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Label rules for the issue:
|
|
163
|
+
|
|
164
|
+
- Always add the `feature` category label (or `refactor` / `bug` when the brief is clearly one of those).
|
|
165
|
+
- Add `enterprise` when `--enterprise` was passed.
|
|
166
|
+
- Add a priority label only when `--priority` was passed (`priority-low` / `priority-medium` / `priority-high` / `priority-extreme`); otherwise leave priority unset (treated as `priority-medium`).
|
|
167
|
+
- Do NOT add pipeline labels (`review`, `qa`, `merge-queue`, …) to the issue — those are PR-only.
|
|
168
|
+
|
|
169
|
+
### 6. Cross-link the artifacts
|
|
170
|
+
|
|
171
|
+
Make the three artifacts point at each other so the trail is navigable:
|
|
172
|
+
|
|
173
|
+
- Edit the spec PR body (or post a comment) so the `Tracking issue` line resolves to the real issue URL.
|
|
174
|
+
- The issue already links the spec path and spec PR from step 5.
|
|
175
|
+
- Optionally add a one-line reference to the issue at the top of the spec (e.g. `Tracking issue: #{n}`), committed and pushed to the PR branch.
|
|
176
|
+
|
|
177
|
+
### 7. Cleanup and report back
|
|
178
|
+
|
|
179
|
+
Clean up any worktree you created (per `.ai/skills/om-auto-create-pr/SKILL.md` step 13). Then report:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
prepare-issue: {brief}
|
|
183
|
+
Spec: {SPEC_PATH}
|
|
184
|
+
Spec PR: {url}
|
|
185
|
+
Tracking issue: {url | skipped (--no-issue) | reused #{n}}
|
|
186
|
+
Branch: {branch}
|
|
187
|
+
Status: {spec-and-issue ready | open-questions pending — answer to continue}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
If the Open Questions gate is still pending, say so explicitly and list the unanswered questions — do not claim the spec is complete.
|
|
191
|
+
|
|
192
|
+
## Rules
|
|
193
|
+
|
|
194
|
+
- This skill captures deferred work only. NEVER implement the feature, write module code, or run migrations. The only file added is the spec.
|
|
195
|
+
- Always write the spec with `om-spec-writing` standards, including the Open Questions hard gate and the integration-coverage section.
|
|
196
|
+
- The spec PR is docs-only: run only the docs-only validation gate, never the full code gate.
|
|
197
|
+
- Spec PR labels are `review`, `documentation`, `skip-qa` — never `needs-qa`. Post a short comment after each label.
|
|
198
|
+
- Base branch is always `develop`. Branch uses the `feat/prepare-<slug>` prefix; never `codex/`.
|
|
199
|
+
- The tracking issue MUST link the spec path and the spec PR, and name the recommended implementer skill (`om-implement-spec` / `om-auto-fix-github`).
|
|
200
|
+
- Search for an existing tracking issue before creating a new one; reuse via comment if a credible duplicate exists.
|
|
201
|
+
- Respect existing claims/locks per `om-auto-create-pr` step 0 and `om-auto-fix-github` step 0; never silently clobber another actor's branch, spec file, or issue.
|
|
202
|
+
- Never paste secrets, tokens, or `.env` content into the spec, PR, or issue.
|
|
@@ -20,7 +20,7 @@ Every item must be answered in the spec or marked N/A with justification.
|
|
|
20
20
|
## 3. Data Integrity & Security
|
|
21
21
|
|
|
22
22
|
- [ ] Entities include `id`, `organization_id`, `tenant_id`, `created_at`, `updated_at`, `deleted_at`, `is_active`
|
|
23
|
-
- [ ] Write operations define transaction boundaries
|
|
23
|
+
- [ ] Write operations define transaction boundaries — specs touching multi-phase scalar + relation writes MUST name `withAtomicFlush({ transaction: true })` and declare that cache invalidation / side effects (`emitCrudSideEffects`) fire AFTER commit (outside the flush block)
|
|
24
24
|
- [ ] Input validation uses zod schemas
|
|
25
25
|
- [ ] All user input validated before business logic/persistence
|
|
26
26
|
- [ ] Auth guards declared per-method in `metadata` (`requireAuth`, `requireFeatures`) — never legacy top-level `export const requireAuth`
|
|
@@ -135,4 +135,4 @@ await withAtomicFlush(em, [
|
|
|
135
135
|
await emitCrudSideEffects({ ... })
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
Never run `em.find`/`em.findOne` between scalar mutations and `em.flush()` without `withAtomicFlush` — changes will be silently lost.
|
|
138
|
+
Never run `em.find`/`em.findOne` between scalar mutations and `em.flush()` without `withAtomicFlush` — changes will be silently lost. Cache invalidation must also stay outside `withAtomicFlush` and fire after commit, so the opt-in `OM_CACHE_SAFETY_ALWAYS_CONSISTENT` mode (default OFF) never serves stale or partially-committed reads.
|
|
@@ -154,12 +154,35 @@ When the user asks to **create a new application** or a **new module**, do not i
|
|
|
154
154
|
| **Multi-tenant scoping (default for every entity)** | Every tenant-scoped entity MUST include indexed `organization_id` and `tenant_id` columns and every read/write MUST filter by them. The CRUD factory injects scope automatically — do NOT bypass it. For ad-hoc queries use `withScopedPayload` from `@open-mercato/shared/lib/api/scoped` | `.ai/skills/om-data-model-design/SKILL.md`; <https://docs.open-mercato.dev/architecture/system-overview> |
|
|
155
155
|
| **Encryption maps for sensitive data** | Declare a module-level `<module>/encryption.ts` exporting `defaultEncryptionMaps: ModuleEncryptionMap[]` from `@open-mercato/shared/modules/encryption`. Read encrypted columns via `findWithDecryption` / `findOneWithDecryption` from `@open-mercato/shared/lib/encryption/find`. NEVER hand-roll AES/KMS, NEVER use `em.find` on encrypted columns | `.ai/skills/om-data-model-design/SKILL.md` → Sensitive Data and Encryption Maps; <https://docs.open-mercato.dev/user-guide/encryption> |
|
|
156
156
|
| **Cache** | Resolve the cache from DI (`container.resolve('cache')`) — never `new Redis(...)` or raw SQLite. Tag with `tenant:<id>` / `org:<id>` and the entity-type tag so invalidation stays tenant-scoped | `.ai/guides/cache.md`; <https://docs.open-mercato.dev/user-guide/cache-management> |
|
|
157
|
+
| **Entity update safety** | Multi-phase scalar + relation mutations use `withAtomicFlush(em, phases, { transaction: true })` from `@open-mercato/shared/lib/commands/flush` — never interleave `em.find`/`em.findOne` between a scalar mutation and `em.flush()`. Keep `emitCrudSideEffects` + cache invalidation OUTSIDE it (they fire after commit) | See **Entity Update Safety** section below |
|
|
157
158
|
| **Background workers** | `src/modules/<id>/workers/*.ts` exporting `metadata: { queue, id?, concurrency? }` + default handler. Never spin up custom queues | `.ai/guides/queue.md`; <https://docs.open-mercato.dev/framework/events/queue-workers> |
|
|
158
159
|
| **Events between modules** | `<module>/events.ts` with `createModuleEvents({ moduleId, events } as const)`. Subscribers in `subscribers/*.ts`. Never call other modules' services directly across module boundaries | `.ai/guides/events.md`; <https://docs.open-mercato.dev/framework/events/overview> |
|
|
159
160
|
| **i18n (every user-facing string)** | `useT()` client-side from `@open-mercato/shared/lib/i18n/context`, `resolveTranslations()` server-side from `@open-mercato/shared/lib/i18n/server`; keys in `src/i18n/<locale>.json`. Never hard-code labels in components | `.ai/guides/shared.md` → i18n |
|
|
160
161
|
|
|
161
162
|
> Rule of thumb: if you find yourself reaching for raw `fetch`, raw `<form>`, ad-hoc `crypto`, ad-hoc `Redis`, or a manual `m2m` join across modules, stop and check the row above — there is a canonical helper.
|
|
162
163
|
|
|
164
|
+
## Entity Update Safety
|
|
165
|
+
|
|
166
|
+
MikroORM v7 can silently discard pending scalar changes when a query (`em.find`, `em.findOne`, sync helper) runs on the same `EntityManager` between a scalar mutation and `em.flush()`. For any command in `{{PROJECT_NAME}}` that mutates entities across multiple phases:
|
|
167
|
+
|
|
168
|
+
- MUST use `withAtomicFlush(em, phases, { transaction: true })` from `@open-mercato/shared/lib/commands/flush`.
|
|
169
|
+
- NEVER interleave `em.find`/`em.findOne`/sync helpers between a scalar mutation and `em.flush()` on the same `EntityManager` without `withAtomicFlush` — the UPDATE is dropped.
|
|
170
|
+
- Keep `emitCrudSideEffects` (and `emitCrudUndoSideEffects`) AND cache invalidation OUTSIDE the `withAtomicFlush` block — they fire only AFTER the DB write commits.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
174
|
+
|
|
175
|
+
await withAtomicFlush(em, [
|
|
176
|
+
() => { record.name = 'New Name'; record.status = 'active' },
|
|
177
|
+
() => syncEntityTags(em, record, tags),
|
|
178
|
+
], { transaction: true, label: '<module>.<command>' })
|
|
179
|
+
|
|
180
|
+
// Side effects + cache invalidation AFTER the atomic flush (post-commit)
|
|
181
|
+
await emitCrudSideEffects({ /* ... */ })
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Because invalidation runs post-commit and the query-index read-projection tail (search tokens, vectors, fulltext, coverage) converges asynchronously, reads can briefly see a convergence window after a write. An opt-in env flag, `OM_CACHE_SAFETY_ALWAYS_CONSISTENT` (default OFF, backward compatible), is planned to make that tail converge synchronously on write at a write-latency cost — treat it as opt-in/forthcoming, not on by default.
|
|
185
|
+
|
|
163
186
|
## CRITICAL rules — always follow without exception
|
|
164
187
|
|
|
165
188
|
1. **Entity classes live in `src/modules/<module>/data/entities.ts` and MUST import decorators from `@mikro-orm/decorators/legacy`.** Start there for every schema change.
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
- [ ] UUID primary keys with standard columns (`id`, `created_at`, `updated_at`)
|
|
26
26
|
- [ ] Soft delete via `deleted_at` where applicable
|
|
27
27
|
- [ ] Atomic transactions for multi-step writes
|
|
28
|
+
- [ ] `withAtomicFlush(em, phases, { transaction: true })` used when mutating across phases that include queries on the same `EntityManager`
|
|
29
|
+
- [ ] No `em.find`/`em.findOne`/sync helpers between a scalar mutation and `em.flush()` without `withAtomicFlush`
|
|
28
30
|
|
|
29
31
|
## 4. API Routes
|
|
30
32
|
|
|
@@ -42,6 +44,7 @@
|
|
|
42
44
|
- [ ] Workers export `metadata` with `{ queue, id?, concurrency? }`
|
|
43
45
|
- [ ] All mutations implemented as commands with undo logic
|
|
44
46
|
- [ ] Side effects outside `withAtomicFlush`
|
|
47
|
+
- [ ] Cache invalidation and side effects (`emitCrudSideEffects`) fire AFTER the DB write commits — never inside the `withAtomicFlush` block
|
|
45
48
|
|
|
46
49
|
## 6. UI & Backend Pages
|
|
47
50
|
|
|
@@ -443,6 +443,8 @@ const items = await em.find(Entity, {
|
|
|
443
443
|
})
|
|
444
444
|
```
|
|
445
445
|
|
|
446
|
+
> **Multi-phase or relation-syncing writes:** the bare `em.flush()` above is fine for a single scalar update. As soon as a write mutates across multiple phases or runs a query (`em.find`/`em.findOne`/sync helper) between a scalar mutation and the flush, switch to `withAtomicFlush(em, phases, { transaction: true })` from `@open-mercato/shared/lib/commands/flush` — MikroORM v7 silently drops the scalar UPDATE otherwise. Never query between scalar mutations and flush; keep side effects + cache invalidation outside the flush (after commit).
|
|
447
|
+
|
|
446
448
|
### Audit/History Table
|
|
447
449
|
|
|
448
450
|
For tracking changes to an entity:
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
| Module ID | plural, snake_case | `fleet_vehicles`, `loyalty_points` |
|
|
8
8
|
| Module folder | same as module ID | `src/modules/fleet_vehicles/` |
|
|
9
9
|
| Entity class | PascalCase, singular | `FleetVehicle`, `LoyaltyPoint` |
|
|
10
|
-
| Entity file |
|
|
10
|
+
| Entity file | single `data/entities.ts` (one class per entity) | `data/entities.ts` → `class FleetVehicle` |
|
|
11
11
|
| Table name | plural, snake_case | `fleet_vehicles`, `loyalty_points` |
|
|
12
12
|
| Column name | snake_case | `vehicle_type`, `point_balance` |
|
|
13
13
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|---------|-----------|---------|
|
|
18
18
|
| JS/TS fields | camelCase | `vehicleType`, `pointBalance` |
|
|
19
19
|
| Event ID | `module.entity.action` (dots, singular entity, past tense) | `fleet_vehicles.vehicle.created` |
|
|
20
|
-
| Feature ID | `module.action` | `fleet_vehicles.view`, `fleet_vehicles.
|
|
20
|
+
| Feature ID | `module.entity.action` (per-entity; use `view` / `manage`) | `fleet_vehicles.vehicle.view`, `fleet_vehicles.vehicle.manage` |
|
|
21
21
|
| Enricher ID | `module.enricher-name` | `fleet_vehicles.maintenance-stats` |
|
|
22
22
|
| Widget ID | `module.injection.widget-name` | `fleet_vehicles.injection.status-column` |
|
|
23
23
|
| Interceptor ID | `module.interceptor-name` | `fleet_vehicles.validate-vin` |
|
|
@@ -39,12 +39,11 @@ deleted_at: Date | null // Soft delete timestamp
|
|
|
39
39
|
|
|
40
40
|
## API Routes
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
|
45
|
-
|
|
46
|
-
|
|
|
47
|
-
| DELETE | `api/delete/<entities>.ts` | `DELETE /api/<module>/<entities>` |
|
|
42
|
+
All HTTP methods live in a **single** `api/<entities>/route.ts` that exports named handlers `{ GET, POST, PUT, DELETE }` + `metadata` + `openApi` (not separate `api/get/`, `api/post/` files).
|
|
43
|
+
|
|
44
|
+
| File Path | Methods | URL |
|
|
45
|
+
|-----------|---------|-----|
|
|
46
|
+
| `api/<entities>/route.ts` | `GET` / `POST` / `PUT` / `DELETE` | `/api/<module>/<entities>` |
|
|
48
47
|
|
|
49
48
|
## Backend Pages
|
|
50
49
|
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: om-prepare-issue
|
|
3
|
+
description: Capture a feature the user wants built later without building it now. Researches and writes a spec via the om-spec-writing conventions, ships it as a docs-only spec PR against `develop` (reusing om-auto-create-pr mechanics; `skip-qa`, `documentation`), then opens a tracking GitHub issue that links the spec path and the spec PR so the work can be picked up later with om-auto-fix-github or om-implement-spec. Use for "park this idea", "write a spec and an issue for later", "prepare an issue to build X eventually", "spec it out but don't implement yet".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Prepare Issue (deferred work)
|
|
7
|
+
|
|
8
|
+
Turn a "we want this eventually" brief into durable, actionable backlog without implementing it. The deliverable is three linked artifacts:
|
|
9
|
+
|
|
10
|
+
1. A **spec** under `.ai/specs/` written to `om-spec-writing` standards.
|
|
11
|
+
2. A **docs-only spec PR** against `develop` that adds only that spec file (`documentation`, `skip-qa`).
|
|
12
|
+
3. A **GitHub issue** to implement the spec, linking both the spec path and the spec PR.
|
|
13
|
+
|
|
14
|
+
This skill is for **deferred** work. It does NOT implement the feature. If the user wants the feature built now, hand off to `om-auto-create-pr` (free-form task) or `om-implement-spec` (after the spec exists) instead.
|
|
15
|
+
|
|
16
|
+
This skill reuses the worktree/branch/commit/label discipline of `.ai/skills/om-auto-create-pr/SKILL.md` for the PR, the spec methodology of `.ai/skills/om-spec-writing/SKILL.md` for the spec, and the issue-claim/linking conventions of `.ai/skills/om-auto-fix-github/SKILL.md` for the tracking issue. Read those before deviating.
|
|
17
|
+
|
|
18
|
+
## Arguments
|
|
19
|
+
|
|
20
|
+
- `{brief}` (required) — free-form description of the feature to capture. One sentence or several paragraphs.
|
|
21
|
+
- `--slug <kebab-case>` (optional) — override the slug used in the spec filename and branch. Default: derived from the brief.
|
|
22
|
+
- `--enterprise` (optional) — write the spec under `.ai/specs/enterprise/` instead of `.ai/specs/` (commercial scope). Default: OSS scope (`.ai/specs/`).
|
|
23
|
+
- `--priority <low|medium|high|extreme>` (optional) — priority label for the tracking issue. Default: unset (treated as `priority-medium`).
|
|
24
|
+
- `--no-issue` (optional) — write the spec and open the spec PR, but skip issue creation. Use when the user only wants the spec on record.
|
|
25
|
+
- `--force` (optional) — bypass the claim-conflict check when a previous run left a branch or spec file behind.
|
|
26
|
+
|
|
27
|
+
## Workflow
|
|
28
|
+
|
|
29
|
+
### 0. Pre-flight and claim
|
|
30
|
+
|
|
31
|
+
Follow `.ai/skills/om-auto-create-pr/SKILL.md` step 0 verbatim. This is new docs work, so the branch MUST use the `feat/` prefix.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
CURRENT_USER=$(gh api user --jq '.login')
|
|
35
|
+
DATE=$(date -u +%Y-%m-%d)
|
|
36
|
+
SLUG="{slug-or-derived}"
|
|
37
|
+
SPEC_DIR=".ai/specs" # or .ai/specs/enterprise when --enterprise
|
|
38
|
+
SPEC_PATH="${SPEC_DIR}/${DATE}-${SLUG}.md"
|
|
39
|
+
BRANCH="feat/prepare-${SLUG}"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A run is **already in progress** when ANY of these is true (treat as re-entry if the current user owns it, otherwise STOP and ask via `AskUserQuestion` unless `--force`):
|
|
43
|
+
|
|
44
|
+
- A file at `$SPEC_PATH` already exists on `origin/develop` or any remote branch.
|
|
45
|
+
- A remote branch `origin/${BRANCH}` already exists.
|
|
46
|
+
- An open PR already adds `$SPEC_PATH`.
|
|
47
|
+
- An open issue already tracks the same feature (search before creating — see step 5).
|
|
48
|
+
|
|
49
|
+
### 1. Triage the brief before writing
|
|
50
|
+
|
|
51
|
+
Read enough context to write a credible spec, not to build it:
|
|
52
|
+
|
|
53
|
+
- Match the brief to rows in the root `AGENTS.md` Task Router and read every matching guide (module, UI, search, events, etc.).
|
|
54
|
+
- Check `.ai/specs/` and `.ai/specs/enterprise/` for an existing spec covering the same area. If one exists, extend or supersede it instead of duplicating — confirm the direction with the user via `AskUserQuestion`.
|
|
55
|
+
- Skim `.ai/lessons.md`.
|
|
56
|
+
|
|
57
|
+
Reduce the brief to: goal in one sentence, affected modules/packages, and the rough scope. If a wrong assumption would force a spec rewrite, surface it as an Open Question (see step 2) rather than guessing.
|
|
58
|
+
|
|
59
|
+
### 2. Write the spec with om-spec-writing
|
|
60
|
+
|
|
61
|
+
Follow `.ai/skills/om-spec-writing/SKILL.md` end to end. Key points for this skill:
|
|
62
|
+
|
|
63
|
+
- Create the spec at `$SPEC_PATH` (`{YYYY-MM-DD}-{kebab-title}.md`, `date` UTC). Enterprise scope goes under `.ai/specs/enterprise/`.
|
|
64
|
+
- Start with a **Skeleton Spec** (TLDR + 2-3 key sections). If critical unknowns exist, add a numbered **Open Questions** block right after the TLDR and **STOP** — ask the user before filling in the rest. This is a hard gate; do not invent answers to architecture-blocking questions.
|
|
65
|
+
- After answers land, expand the spec: Problem Statement, Proposed Solution, Phasing (stories), Implementation Plan (testable Steps), and the architectural concerns the `om-spec-writing` lens requires (singular naming, FK-only cross-module links, `organization_id` scoping, undoability, zod validation, encryption maps for sensitive columns, canonical primitives, Design System tokens, Frontend Architecture Contract when UI is touched).
|
|
66
|
+
- The spec MUST include an **integration coverage** section listing the affected API paths and key UI paths the implementer will need to test (root `AGENTS.md` → Documentation and Specifications requires this for every feature).
|
|
67
|
+
- Apply the `om-spec-writing` Spec Checklist and Final Compliance Review before finalizing.
|
|
68
|
+
|
|
69
|
+
Do NOT write any implementation code, migrations, or module files. The only file this run adds is the spec.
|
|
70
|
+
|
|
71
|
+
### 3. Isolated worktree, branch, and first commit
|
|
72
|
+
|
|
73
|
+
Follow `.ai/skills/om-auto-create-pr/SKILL.md` steps 4–5 verbatim. Base is always `develop`. Commit the spec as the first commit, then push.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git add "$SPEC_PATH"
|
|
77
|
+
git commit -m "docs(spec): add ${SLUG} spec for deferred implementation"
|
|
78
|
+
git push -u origin "$BRANCH"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
If the Open Questions gate in step 2 is still unresolved, do not create the worktree or push — resolve the questions with the user first.
|
|
82
|
+
|
|
83
|
+
### 4. Open the spec PR
|
|
84
|
+
|
|
85
|
+
This is a docs-only / spec-only PR. The minimum validation gate is the docs-only gate from `.ai/skills/om-auto-create-pr/SKILL.md` step 7:
|
|
86
|
+
|
|
87
|
+
- `yarn lint` if it catches markdown/YAML issues.
|
|
88
|
+
- `git diff --check` — no trailing whitespace, no merge markers.
|
|
89
|
+
- A manual re-read of the spec.
|
|
90
|
+
|
|
91
|
+
Never run the full code gate (`yarn test` / `yarn typecheck` / `yarn build:app`) for a spec-only run.
|
|
92
|
+
|
|
93
|
+
Open the PR against `develop`:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
gh pr create --base develop \
|
|
97
|
+
--title "docs(spec): ${SLUG} — deferred implementation spec" \
|
|
98
|
+
--body-file <(cat <<'EOF'
|
|
99
|
+
## Goal
|
|
100
|
+
- {one-line feature summary from the brief}
|
|
101
|
+
|
|
102
|
+
## What this PR adds
|
|
103
|
+
- Spec only: `{SPEC_PATH}`. No implementation, no code, no migrations.
|
|
104
|
+
|
|
105
|
+
## Why now
|
|
106
|
+
- Captures deferred work as a reviewable spec so it can be implemented later via `om-implement-spec` / `om-auto-fix-github`.
|
|
107
|
+
|
|
108
|
+
## Tracking issue
|
|
109
|
+
- {issue URL — filled in after step 5, or "see follow-up comment"}
|
|
110
|
+
|
|
111
|
+
## Backward Compatibility
|
|
112
|
+
- No contract surface changes (spec document only).
|
|
113
|
+
EOF
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Apply labels per root `AGENTS.md` PR workflow, each followed by a short explanatory comment (per the `om-auto-create-pr` label-comment rule):
|
|
118
|
+
|
|
119
|
+
- `review` — "PR is ready for code review."
|
|
120
|
+
- `documentation` — "spec-only deliverable under `.ai/specs/`."
|
|
121
|
+
- `skip-qa` — "spec/docs-only; no customer-facing runtime behavior."
|
|
122
|
+
|
|
123
|
+
Never add `needs-qa` to a prepare-issue PR — it adds no runtime behavior to exercise.
|
|
124
|
+
|
|
125
|
+
### 5. Create the tracking issue
|
|
126
|
+
|
|
127
|
+
Skip this step only when `--no-issue` was passed.
|
|
128
|
+
|
|
129
|
+
First, avoid duplicates — search for an existing tracking issue:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
gh issue list --state open --search "${SLUG} in:title,body" --json number,title,url
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If a credible duplicate exists, link the spec/PR in a comment on that issue instead of opening a new one, and report which issue you reused.
|
|
136
|
+
|
|
137
|
+
Otherwise create the issue. It MUST link the spec path and the spec PR, and name the recommended implementer skill so a future run can pick it up:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
gh issue create \
|
|
141
|
+
--title "Implement: {feature title}" \
|
|
142
|
+
--label "feature" \
|
|
143
|
+
--body-file <(cat <<'EOF'
|
|
144
|
+
## Summary
|
|
145
|
+
- {one-line feature summary from the brief}
|
|
146
|
+
|
|
147
|
+
## Spec
|
|
148
|
+
- Implementation spec: `{SPEC_PATH}`
|
|
149
|
+
- Spec PR: {spec PR URL}
|
|
150
|
+
|
|
151
|
+
## How to implement
|
|
152
|
+
- Once the spec PR merges, run `/om-implement-spec {SPEC_PATH}` (multi-phase) or `/om-auto-fix-github {thisIssueNumber}` (smaller scope).
|
|
153
|
+
- Do not start implementation until the spec PR is merged into `develop`.
|
|
154
|
+
|
|
155
|
+
## Scope notes
|
|
156
|
+
- {affected modules/packages}
|
|
157
|
+
- {non-goals captured in the spec}
|
|
158
|
+
EOF
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Label rules for the issue:
|
|
163
|
+
|
|
164
|
+
- Always add the `feature` category label (or `refactor` / `bug` when the brief is clearly one of those).
|
|
165
|
+
- Add `enterprise` when `--enterprise` was passed.
|
|
166
|
+
- Add a priority label only when `--priority` was passed (`priority-low` / `priority-medium` / `priority-high` / `priority-extreme`); otherwise leave priority unset (treated as `priority-medium`).
|
|
167
|
+
- Do NOT add pipeline labels (`review`, `qa`, `merge-queue`, …) to the issue — those are PR-only.
|
|
168
|
+
|
|
169
|
+
### 6. Cross-link the artifacts
|
|
170
|
+
|
|
171
|
+
Make the three artifacts point at each other so the trail is navigable:
|
|
172
|
+
|
|
173
|
+
- Edit the spec PR body (or post a comment) so the `Tracking issue` line resolves to the real issue URL.
|
|
174
|
+
- The issue already links the spec path and spec PR from step 5.
|
|
175
|
+
- Optionally add a one-line reference to the issue at the top of the spec (e.g. `Tracking issue: #{n}`), committed and pushed to the PR branch.
|
|
176
|
+
|
|
177
|
+
### 7. Cleanup and report back
|
|
178
|
+
|
|
179
|
+
Clean up any worktree you created (per `.ai/skills/om-auto-create-pr/SKILL.md` step 13). Then report:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
prepare-issue: {brief}
|
|
183
|
+
Spec: {SPEC_PATH}
|
|
184
|
+
Spec PR: {url}
|
|
185
|
+
Tracking issue: {url | skipped (--no-issue) | reused #{n}}
|
|
186
|
+
Branch: {branch}
|
|
187
|
+
Status: {spec-and-issue ready | open-questions pending — answer to continue}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
If the Open Questions gate is still pending, say so explicitly and list the unanswered questions — do not claim the spec is complete.
|
|
191
|
+
|
|
192
|
+
## Rules
|
|
193
|
+
|
|
194
|
+
- This skill captures deferred work only. NEVER implement the feature, write module code, or run migrations. The only file added is the spec.
|
|
195
|
+
- Always write the spec with `om-spec-writing` standards, including the Open Questions hard gate and the integration-coverage section.
|
|
196
|
+
- The spec PR is docs-only: run only the docs-only validation gate, never the full code gate.
|
|
197
|
+
- Spec PR labels are `review`, `documentation`, `skip-qa` — never `needs-qa`. Post a short comment after each label.
|
|
198
|
+
- Base branch is always `develop`. Branch uses the `feat/prepare-<slug>` prefix; never `codex/`.
|
|
199
|
+
- The tracking issue MUST link the spec path and the spec PR, and name the recommended implementer skill (`om-implement-spec` / `om-auto-fix-github`).
|
|
200
|
+
- Search for an existing tracking issue before creating a new one; reuse via comment if a credible duplicate exists.
|
|
201
|
+
- Respect existing claims/locks per `om-auto-create-pr` step 0 and `om-auto-fix-github` step 0; never silently clobber another actor's branch, spec file, or issue.
|
|
202
|
+
- Never paste secrets, tokens, or `.env` content into the spec, PR, or issue.
|
|
@@ -20,7 +20,7 @@ Every item must be answered in the spec or marked N/A with justification.
|
|
|
20
20
|
## 3. Data Integrity & Security
|
|
21
21
|
|
|
22
22
|
- [ ] Entities include `id`, `organization_id`, `tenant_id`, `created_at`, `updated_at`, `deleted_at`, `is_active`
|
|
23
|
-
- [ ] Write operations define transaction boundaries
|
|
23
|
+
- [ ] Write operations define transaction boundaries — specs touching multi-phase scalar + relation writes MUST name `withAtomicFlush({ transaction: true })` and declare that cache invalidation / side effects (`emitCrudSideEffects`) fire AFTER commit (outside the flush block)
|
|
24
24
|
- [ ] Input validation uses zod schemas
|
|
25
25
|
- [ ] All user input validated before business logic/persistence
|
|
26
26
|
- [ ] Auth guards declared per-method in `metadata` (`requireAuth`, `requireFeatures`) — never legacy top-level `export const requireAuth`
|
package/dist/index.js
CHANGED
|
@@ -571,7 +571,8 @@ function generateShared(config) {
|
|
|
571
571
|
"om-auto-create-pr-loop",
|
|
572
572
|
"om-auto-continue-pr-loop",
|
|
573
573
|
"om-auto-review-pr",
|
|
574
|
-
"om-auto-fix-github"
|
|
574
|
+
"om-auto-fix-github",
|
|
575
|
+
"om-prepare-issue"
|
|
575
576
|
]) {
|
|
576
577
|
if (!existsSync2(join3(AGENTIC_DIR, "ai", "skills", autoSkill, "SKILL.md"))) {
|
|
577
578
|
continue;
|
|
@@ -836,6 +837,7 @@ function printSummary(selectedIds) {
|
|
|
836
837
|
console.log(" /om-auto-continue-pr <PR#> \u2014 resume an in-progress agent PR");
|
|
837
838
|
console.log(" /om-auto-review-pr <PR#> \u2014 automated code review (optional autofix)");
|
|
838
839
|
console.log(" /om-auto-fix-github <issue#> \u2014 fix a GitHub issue and open a PR");
|
|
840
|
+
console.log(" /om-prepare-issue <idea> \u2014 spec out deferred work + open a tracking issue (no build)");
|
|
839
841
|
console.log(" /om-trim-unused-modules \u2014 slim classic-mode defaults after adding your own module");
|
|
840
842
|
console.log(" See .ai/skills/om-auto-create-pr/STANDALONE.md for portability notes");
|
|
841
843
|
console.log(" (base-branch discovery, opt-in pipeline labels, script probing).");
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -268,6 +268,28 @@ Practical consequences:
|
|
|
268
268
|
4. Re-apply (`yarn db:migrate`) and commit.
|
|
269
269
|
- Never hand-edit historical migrations that have shipped; add a **new** migration that performs the correction instead.
|
|
270
270
|
|
|
271
|
+
## Entity Update Safety / Transaction Safety
|
|
272
|
+
|
|
273
|
+
MikroORM v7 can silently drop a pending scalar UPDATE when a query (`em.find`, `em.findOne`, or a sync helper) runs on the same `EntityManager` between the scalar mutation and `em.flush()`. For commands that mutate entities across multiple phases:
|
|
274
|
+
|
|
275
|
+
- Use `withAtomicFlush(em, phases, { transaction: true })` from `@open-mercato/shared/lib/commands/flush` for multi-phase scalar + relation mutations.
|
|
276
|
+
- Never interleave `em.find`/`em.findOne` between a scalar mutation and `em.flush()` without `withAtomicFlush`.
|
|
277
|
+
- Keep `emitCrudSideEffects` and cache invalidation OUTSIDE the `withAtomicFlush` block — they run only AFTER the DB write commits.
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
281
|
+
|
|
282
|
+
await withAtomicFlush(em, [
|
|
283
|
+
() => { record.name = 'New Name'; record.status = 'active' },
|
|
284
|
+
() => syncEntityTags(em, record, tags),
|
|
285
|
+
], { transaction: true })
|
|
286
|
+
|
|
287
|
+
// Cache invalidation + side effects AFTER commit
|
|
288
|
+
await emitCrudSideEffects({ /* ... */ })
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Cache invalidation running post-commit means reads can briefly observe a query-index convergence window. The opt-in `OM_CACHE_SAFETY_ALWAYS_CONSISTENT` env flag (default OFF, backward compatible) is planned to make the query-index read-projection tail converge synchronously on write, at a write-latency cost — opt-in/forthcoming, not on by default.
|
|
292
|
+
|
|
271
293
|
## AI Assistant — adding agents, tools, UI parts, and overrides
|
|
272
294
|
|
|
273
295
|
Standalone apps consume the AI framework from `@open-mercato/ai-assistant` (in `node_modules/`). The same conventions used in the monorepo apply here:
|