@dforge-core/dforge-mcp 0.1.13 → 0.2.0
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/CHANGELOG.md +231 -0
- package/README.md +106 -72
- package/dist/server.js +2315 -388
- package/docs/creating-modules.md +21 -10
- package/package.json +7 -4
- package/scripts/install-skills.mjs +165 -0
- package/skills/dforge-mcp-author/SKILL.md +147 -471
- package/skills/dforge-module-build/SKILL.md +309 -0
- package/skills/dforge-module-design/SKILL.md +125 -0
- package/skills/dforge-module-ship/SKILL.md +163 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dforge-module-build
|
|
3
|
+
description: Phases 1-5 of dForge module authoring — scaffold the module, then build entities and fields, relations, roll-ups and status columns, actions/triggers/jobs/webhooks, data views and menus, reports, settings, translations, seed data, and the security roles and rights matrix. Use when dforge_module_plan reports a currentPhase of 1-5, or when adding to / fixing an existing module's entities, views, or security. Not for the design documents (dforge-module-design) or for packing and installing (dforge-module-ship).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dForge module — Build (Phases 1–5)
|
|
7
|
+
|
|
8
|
+
Phase 0 is done and `readyToScaffold: true`. You're now producing the actual
|
|
9
|
+
module artifacts.
|
|
10
|
+
|
|
11
|
+
**Before each phase:** run `dforge_module_inspect` and parse
|
|
12
|
+
`files["_inspect.json"]` — never plan a patch against what you assume is there.
|
|
13
|
+
**After each phase:** record it with
|
|
14
|
+
`dforge_module_plan({ action: "complete_phase", moduleDir, phase: "N", note: "..." })`.
|
|
15
|
+
|
|
16
|
+
## Loading policy — one reference per step, at the step
|
|
17
|
+
|
|
18
|
+
**Load nothing up front.** Pull the row you need when you need it, by URI.
|
|
19
|
+
Re-load on every backtrack into that element type. The example files are
|
|
20
|
+
mandatory structure validators — never work from memory for schema shapes,
|
|
21
|
+
flags, or column patterns.
|
|
22
|
+
|
|
23
|
+
| When you need to… | Load (primary reference + example) | Also load |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| Add any field | `dforge://reference/field-types`, `dforge://reference/flags`, `dforge://example/entities/todo_item.json` | — |
|
|
26
|
+
| Add a relation / Set column | `dforge://reference/column-types`, `dforge://example/entities/todo_item.json` | — |
|
|
27
|
+
| Reuse a type/enum across columns (a `domain`) | `dforge://reference/column-domains` | `dforge://schema/domains` |
|
|
28
|
+
| Add a formula column | `dforge://reference/formulas` | — |
|
|
29
|
+
| Add a trait | `dforge://reference/traits` | — |
|
|
30
|
+
| Add a number sequence | `dforge://reference/number-sequences` | — |
|
|
31
|
+
| Add a grid / list view | `dforge://reference/data-views`, `dforge://example/ui/data_views.json` | — |
|
|
32
|
+
| Add a matrix (pivot) view | `dforge://reference/data-views` (§Matrix), `dforge://example/matrix-budget/ui/data_views.json`, `dforge://example/matrix-budget/entities/budget_line.json` | — |
|
|
33
|
+
| Add kanban / calendar / tree-grid / master-detail | `dforge://reference/data-views` | `dforge://schema/data-views` |
|
|
34
|
+
| Add a menu | `dforge://reference/menus`, `dforge://example/ui/menus.json` | — |
|
|
35
|
+
| Add an action | `dforge://reference/action-dsl`, `dforge://example/logic/actions/mark_done.dsl`, `dforge://example/ui/actions.json` | `dforge://docs/dsl` |
|
|
36
|
+
| Add a trigger | — | `dforge://schema/triggers`; condition syntax in `dforge://docs/dsl` |
|
|
37
|
+
| Add a scheduled job | `dforge://reference/jobs` | `dforge://schema/jobs` |
|
|
38
|
+
| Add a webhook | — | `dforge://schema/webhooks` |
|
|
39
|
+
| Add filters (views, folders, reports) | `dforge://reference/filters` | — |
|
|
40
|
+
| Add roles or security folders | `dforge://reference/security`, `dforge://reference/filters`, `dforge://example/security/roles.json` | — |
|
|
41
|
+
| Add a report | `dforge://reference/reports` | `dforge://schema/reports` |
|
|
42
|
+
| Add a print template | `dforge://reference/print-templates` | — |
|
|
43
|
+
| Wire OCR / document extraction | `dforge://reference/document-extraction` | `dforge://docs/dsl` |
|
|
44
|
+
| Add / localize translations | `dforge://reference/translations` | — |
|
|
45
|
+
| Add module settings | `dforge://reference/settings` | — |
|
|
46
|
+
| Add saved queries | `dforge://reference/queries` | — |
|
|
47
|
+
| Import from DBML/SQL | `dforge://reference/schema-import` | — |
|
|
48
|
+
| Import from a spreadsheet | `dforge://reference/excel-import` | — |
|
|
49
|
+
| Migrate a legacy database | `dforge://reference/data-migration` | — |
|
|
50
|
+
|
|
51
|
+
## Core rules — the always-on cheat sheet
|
|
52
|
+
|
|
53
|
+
Enough to author inline; load the linked reference for the full detail.
|
|
54
|
+
|
|
55
|
+
- **Naming.** `code`, entity `dbObject`, column keys: all `snake_case`,
|
|
56
|
+
case-sensitive, entities singular (`opportunity_line`). `code` = DB schema name.
|
|
57
|
+
- **A relation is TWO columns + a references entry.** Use
|
|
58
|
+
`dforge_entity_reference_add` — it emits all three parts. → `column-types`
|
|
59
|
+
- **Flags** are letters from `V I E M H` only (never `U`/`S`/`P`): `VEM`
|
|
60
|
+
required+visible, `VE` optional+visible, `V` read-only, `EM` hidden FK, `I`
|
|
61
|
+
trait-provided. → `flags`
|
|
62
|
+
- **`fieldTypeCd` = UI control; `dbDatatype` = SQL type.** Omit `dbDatatype` on
|
|
63
|
+
plain data columns — it's derived. Set it only for a hidden FK or to override
|
|
64
|
+
size/precision. Common fixes: `number` not `integer`/`float`; `phone` not
|
|
65
|
+
`phoneNumber`; `date` not `datePicker`; `timestamptz` not `datetime`; `bool`
|
|
66
|
+
not `boolean`; `varchar`/`text` not `string`. → `field-types`
|
|
67
|
+
- **Formula columns** (`columnType: "F"`): `baseDatatypeCd` required, no
|
|
68
|
+
`dbDatatype`, `flags: "V"`. → `formulas`
|
|
69
|
+
- **Roll-up totals** over a child set are Generated (`G`) columns with
|
|
70
|
+
`SUM([set].[field])` — **never** Formula (`F`), whose set-aggregates silently
|
|
71
|
+
render empty — and they aggregate only a **physical** child column. Use
|
|
72
|
+
`dforge_entity_rollup_add`. → `column-types`
|
|
73
|
+
- **Column defaults** are set via `formula` (`"TODAY()"`, `"'draft'"`), a
|
|
74
|
+
`numberSequence`, or DSL. Entity fields have **no** `defaultValue` key —
|
|
75
|
+
that's settings-only. → `field-types`
|
|
76
|
+
- **Traits:** default `["identity", "audit"]`. `audit-full` adds required
|
|
77
|
+
`created_by`/`last_updated_by` with no default — a seeded `audit-full` entity
|
|
78
|
+
must set both to `0` in every record, or use plain `audit`. → `traits`
|
|
79
|
+
- **`toString`** on every entity, `{column}` braces: `"{first_name} {last_name}"`.
|
|
80
|
+
- **Data views:** `dataSources` array at root — never a root-level `entityCode` +
|
|
81
|
+
`columns`. Sort is `"order": ["-col", "col"]`. → `data-views`
|
|
82
|
+
- **Menus:** leaf items use `dataViewCode` (not `viewCode`); section nodes omit
|
|
83
|
+
`itemType`; icons are Bootstrap names **without** `bi-`. Use
|
|
84
|
+
`dforge_menu_add`. → `menus`
|
|
85
|
+
- **Rights keys:** same-module entity bare (`product`); cross-module entity
|
|
86
|
+
dotted (`fin.invoice`); actions/reports/folders take a **colon** —
|
|
87
|
+
`action:approve`, `report:summary`, `folder:east` (never a dot). Deny by
|
|
88
|
+
omitting the key, never `""`. `roles.json` carries `description` + `rights`
|
|
89
|
+
only — no `label`. → `security`
|
|
90
|
+
- **Action `script`** in `ui/actions.json` is a bare filename (no path, no
|
|
91
|
+
`.dsl`).
|
|
92
|
+
- **Action DSL dates:** inside `execute:` use lowercase `now()`. `TODAY()`/`NOW()`
|
|
93
|
+
are formula-only and undefined in `execute:`. → `action-dsl`
|
|
94
|
+
- **SQL placeholders** are `@paramName`, not `:paramName`.
|
|
95
|
+
- **Manifest:** non-English locales go in `supportedLocales` (`ll-CC`, never
|
|
96
|
+
`en`/`en-US`); there is no `translations` manifest key. `security` has both
|
|
97
|
+
`roles` and `folders`. → `manifest`
|
|
98
|
+
- **Seed data:** explicit numeric PKs under `{entity}_id`; parents before
|
|
99
|
+
children via the `NN-` prefix. Use `dforge_seed_add`.
|
|
100
|
+
|
|
101
|
+
## Phase 1 — Domain (required)
|
|
102
|
+
|
|
103
|
+
> **Fast on-ramp.** If the user already has the model in **DBML** → use
|
|
104
|
+
> `dforge_dbml_import`. In a **spreadsheet** → load
|
|
105
|
+
> `dforge://reference/excel-import`; a binary `.xlsx` must be decoded first with
|
|
106
|
+
> `dforge://script/xlsx-to-model` (`python3 tmp.py file.xlsx` → `{sheets:[…]}`),
|
|
107
|
+
> then turned into a table-spec for `dforge_module_import`. A `.csv` is plain
|
|
108
|
+
> text — read it directly. Both paths ADD entities; show the proposed table-spec
|
|
109
|
+
> for sign-off first, then run `dforge_module_validate` and refine the generated
|
|
110
|
+
> grids.
|
|
111
|
+
|
|
112
|
+
**This phase's first deliverable, before any tool call, is the proposed entity
|
|
113
|
+
inventory.** The user needs to see "the module will have these N things in it"
|
|
114
|
+
before files exist — entities are the spine everything else references.
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
Proposed entities (N):
|
|
118
|
+
- <entity_code> — <one-line purpose, tied to a verb from intake>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Loop with the user until they explicitly approve.
|
|
122
|
+
|
|
123
|
+
**Pre-scaffold consistency checks** against `docs/DESIGN.md` — if any fail,
|
|
124
|
+
surface it and go back to Phase 0c; don't silently adjust the design:
|
|
125
|
+
|
|
126
|
+
1. Every FK in the relationship map has a corresponding field on the child.
|
|
127
|
+
2. Every action's `canExecute` guard references a status value that exists.
|
|
128
|
+
3. Every seed record's FK points at a parent that is also seeded.
|
|
129
|
+
4. Every formula uses only fields on the same entity or exactly one FK hop away.
|
|
130
|
+
5. Every set aggregate is a Generated column over a physical child column.
|
|
131
|
+
6. Every seeded `audit-full` entity sets `created_by`/`last_updated_by: 0`.
|
|
132
|
+
|
|
133
|
+
Then: `dforge_module_create` → per-entity loop.
|
|
134
|
+
|
|
135
|
+
**Per-entity loop.** For each entity, propose fields + traits + relations, get
|
|
136
|
+
approval, then patch. Reach for the **composite tools first**:
|
|
137
|
+
|
|
138
|
+
- relation → `dforge_entity_reference_add`
|
|
139
|
+
- roll-up total → `dforge_entity_rollup_add`
|
|
140
|
+
- status column → `dforge_entity_status_add`
|
|
141
|
+
- plain scalar → `dforge_entity_field_add`
|
|
142
|
+
|
|
143
|
+
**Field batching rule** — the only Phase 1 exception to one-at-a-time. A field
|
|
144
|
+
is batchable only if ALL of: scalar primitive (string/integer/decimal/boolean/
|
|
145
|
+
date), no FK or reference, no formula, and unambiguous nullability. Everything
|
|
146
|
+
else goes one at a time. This never justifies batching entities, views, roles,
|
|
147
|
+
actions, or reports.
|
|
148
|
+
|
|
149
|
+
**Extension entities last.** `extends: "module.entity"`, `toString: null`,
|
|
150
|
+
dotted manifest key. Snapshot the base entity's fields with
|
|
151
|
+
`dforge_module_inspect` on the dependency dir when it's available locally;
|
|
152
|
+
otherwise document the known base fields and note that drift tracking is the
|
|
153
|
+
user's responsibility.
|
|
154
|
+
|
|
155
|
+
**Exit:** every entity has PK + audit traits + at least one user-visible field;
|
|
156
|
+
FK references resolve; the manifest's `entities` map reflects reality.
|
|
157
|
+
|
|
158
|
+
## Phase 2 — Behavior (all optional)
|
|
159
|
+
|
|
160
|
+
Four kinds of behavior, all individually skippable. **Do not fabricate behavior
|
|
161
|
+
to fill a sub-step** — a pure CRUD module skips Phase 2 entirely (record it with
|
|
162
|
+
`skipped: true`).
|
|
163
|
+
|
|
164
|
+
| Sub-step | Fires when | Tool |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| 2a Actions | user clicks a button | `dforge_action_add` |
|
|
167
|
+
| 2b Triggers | a DB event happens | `dforge_trigger_add` |
|
|
168
|
+
| 2c Jobs | a cron timer fires | `dforge_job_add` |
|
|
169
|
+
| 2d Webhooks | a DB event → outbound POST | `dforge_webhook_add` |
|
|
170
|
+
|
|
171
|
+
**2a Actions.** Load all four resources in the action row — the wrong-field-
|
|
172
|
+
access / wrong-batch-flag / wrong-`ui/actions.json`-property mistakes only
|
|
173
|
+
surface when you cross-check them. Draft the DSL, run
|
|
174
|
+
**`dforge_action_check({ dslBody, executionMode })`** to catch install failures
|
|
175
|
+
before committing, then `dforge_action_add`. One action per turn.
|
|
176
|
+
|
|
177
|
+
**2b Triggers** reference actions that must already exist. `async: true` runs
|
|
178
|
+
after the transaction commits (use for anything slow); `async: false` runs
|
|
179
|
+
in-transaction, so a failure rolls back the original change.
|
|
180
|
+
|
|
181
|
+
**2c Jobs** run as the system user with **no current record** — the action must
|
|
182
|
+
not use `[field]` syntax. Wrap a record-context action in a thin job action that
|
|
183
|
+
uses `select()`/`query()` first. `timeout` is required (≤ 3600s); over 300s you
|
|
184
|
+
must set `jobClass: 'long_running'`. `dforge_module_validate` flags violations.
|
|
185
|
+
|
|
186
|
+
**2d Webhooks.** For authenticated endpoints, put credentials behind
|
|
187
|
+
`getSecret()` and reference them as `"Authorization": "$secret:<secret_cd>"`.
|
|
188
|
+
|
|
189
|
+
## Phase 3 — Views + menus (3a required), reports (optional)
|
|
190
|
+
|
|
191
|
+
**3a. Default grids first.** Every entity gets a `viewType: "grid"` view with
|
|
192
|
+
`dataSources: [{ entityCode, columns: [...] }]`. The scaffolder already wrote one
|
|
193
|
+
per entity, so you'll often `view_modify` rather than `view_add`.
|
|
194
|
+
|
|
195
|
+
View codes are semantic — the entity name (`feedback_item`), a plural
|
|
196
|
+
(`invoices`), or descriptive (`invoices_kanban`). **Never use the literal code
|
|
197
|
+
`default`**; `ui/folders.json`'s `viewName: "default"` is a fallback alias the
|
|
198
|
+
platform resolves to the entity's first declared view.
|
|
199
|
+
|
|
200
|
+
Then wire menus with `dforge_menu_add` — a section node per group, leaves
|
|
201
|
+
pointing at the views.
|
|
202
|
+
|
|
203
|
+
**Do not propose any specialized view until every entity has its grid.**
|
|
204
|
+
|
|
205
|
+
**3b. Specialized views** only when an objective trigger fires:
|
|
206
|
+
|
|
207
|
+
- the user explicitly asked for the visualization;
|
|
208
|
+
- a status/stage/kind field with **3+ discrete values** → kanban;
|
|
209
|
+
- a required date/time field for scheduling → calendar;
|
|
210
|
+
- a self-referencing FK → tree-grid;
|
|
211
|
+
- a 1:N child with `parentSetField` → list-with-levels or master-detail.
|
|
212
|
+
|
|
213
|
+
If none fire, skip specialized views for that entity.
|
|
214
|
+
|
|
215
|
+
**3c. Reports** only when aggregation/grouping isn't covered by views.
|
|
216
|
+
|
|
217
|
+
**Exit:** every entity has a grid and a menu entry; every specialized view has a
|
|
218
|
+
stated trigger.
|
|
219
|
+
|
|
220
|
+
## Phase 4 — Polish (mostly optional)
|
|
221
|
+
|
|
222
|
+
- **Settings** — `dforge_setting_add` per configurable value the user asked for.
|
|
223
|
+
- **Translations** — run **`dforge_translation_sync`**. It generates every
|
|
224
|
+
required key from the module's own contents, never overwrites existing
|
|
225
|
+
translated text, and fills the **role labels that are completeness-enforced at
|
|
226
|
+
install**. Run it after any entity/view/role change, and always before Phase 6.
|
|
227
|
+
For a non-English locale the seeded values are English placeholders — tell the
|
|
228
|
+
user they still need real translation.
|
|
229
|
+
- **Seed data** — `dforge_seed_add`, only when the module needs reference data on
|
|
230
|
+
install. Parents get a lower `order` than children.
|
|
231
|
+
|
|
232
|
+
## Phase 5 — Security (5a required)
|
|
233
|
+
|
|
234
|
+
1. **Inspect first.** The scaffolder pre-created `<code>.admin` with `SIUDC` on
|
|
235
|
+
every entity declared at scaffold time. It exists — don't re-create it
|
|
236
|
+
(`role_add` fails on an existing code; use `role_right_set` to amend).
|
|
237
|
+
2. **Derive roles from the intake's user types and verbs.** Re-read the user-type
|
|
238
|
+
sentences in `docs/REQUIREMENTS.md`. One role per distinct user type, named
|
|
239
|
+
`<code>.<user-type>`. **Forbidden:** a generic admin/contributor/viewer matrix
|
|
240
|
+
the user didn't ask for.
|
|
241
|
+
3. **Reflect the role list back** before computing rights. Get confirmation.
|
|
242
|
+
4. **Show the rights matrix as a table** (rows = entities/actions/reports,
|
|
243
|
+
columns = roles, cells = rights strings), each cell explained by the verb it
|
|
244
|
+
maps to. Get sign-off.
|
|
245
|
+
5. `dforge_role_add` for new roles; `dforge_role_right_set` per grant when
|
|
246
|
+
amending an existing one.
|
|
247
|
+
|
|
248
|
+
Rights are **additive** — multiple roles UNION, never revoke. Entities take any
|
|
249
|
+
subset of `SIUDC`; actions/reports/folders take `E`.
|
|
250
|
+
|
|
251
|
+
**5b. Security folders** only if intake said data must be partitioned
|
|
252
|
+
(multi-warehouse, multi-region). Default: root only.
|
|
253
|
+
|
|
254
|
+
**Folder codes must be unique across the whole tree**, not just among siblings.
|
|
255
|
+
A folder is addressed flat and path-less everywhere outside `ui/folders.json` —
|
|
256
|
+
role rights say `folder:<code>`, translations key on `folders.<code>.label` — so
|
|
257
|
+
two folders both coded `admin` under different parents are ambiguous in the
|
|
258
|
+
rights matrix and collide in the translation files. `dforge_folder_add` refuses
|
|
259
|
+
a duplicate and `dforge_module_validate` errors on one; name them distinctly
|
|
260
|
+
(`north_admin`, `south_admin`).
|
|
261
|
+
|
|
262
|
+
> ⛔ **`dforge_module_pack` refuses to build** if any entity has no role granting
|
|
263
|
+
> `S`. Don't lean on the gate — derive real persona roles here, and grant `E` on
|
|
264
|
+
> the actions/reports each persona uses.
|
|
265
|
+
|
|
266
|
+
**Exit:** every entity appears in at least one role's rights with at least `S`;
|
|
267
|
+
every folder declared has security mapped.
|
|
268
|
+
|
|
269
|
+
## Backtrack protocol
|
|
270
|
+
|
|
271
|
+
When a later phase exposes a problem in an earlier one, in order:
|
|
272
|
+
|
|
273
|
+
1. **Stop.** Don't paper over it.
|
|
274
|
+
2. **Name it precisely.** "Phase 3 wants a kanban grouped by `lead_status`, but
|
|
275
|
+
Phase 1 didn't define `lead_status` on `lead`."
|
|
276
|
+
3. **Identify the target phase + decision.**
|
|
277
|
+
4. **Get user sign-off**, including cascading impacts.
|
|
278
|
+
5. **Apply the smallest tool that fits** — this overrides the phase labels:
|
|
279
|
+
`entity_field_add` over `entity_add`; `entity_field_rename` over remove+add;
|
|
280
|
+
`role_right_set` over `role_add`; `view_modify` over remove+add.
|
|
281
|
+
6. **Re-run `dforge_module_inspect`**, fix knock-on impacts, resume.
|
|
282
|
+
|
|
283
|
+
**Multi-trigger rule:** if several phases expose gaps at once, resolve the
|
|
284
|
+
**earliest-phase** gap first, complete its backtrack, re-inspect, then
|
|
285
|
+
re-evaluate.
|
|
286
|
+
|
|
287
|
+
**Rename and delete are handled for you** — `entity_field_rename`,
|
|
288
|
+
`entity_field_remove`, `entity_rename`, `entity_delete` each propagate the
|
|
289
|
+
cascade in one call. **Always apply the response's `deletes` as well as
|
|
290
|
+
`files`** (or just pass `apply: true`, which guarantees it). Each tool surfaces
|
|
291
|
+
what it could NOT auto-fix — reports, translations, menu labels, DSL bodies,
|
|
292
|
+
dangling cross-entity FKs — in `warning`. **Run `dforge_module_validate` after
|
|
293
|
+
any rename/delete.**
|
|
294
|
+
|
|
295
|
+
After every backtrack, append to `_brief/changelog.md`:
|
|
296
|
+
|
|
297
|
+
```markdown
|
|
298
|
+
## <YYYY-MM-DD> — Phase N → Phase M backtrack
|
|
299
|
+
- Trigger: <what the later phase tried to do>
|
|
300
|
+
- Change: <what was patched>
|
|
301
|
+
- Affected files: <list>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Exit
|
|
305
|
+
|
|
306
|
+
When Phases 1, 3a and 5a are done (and 2/4 done or recorded as skipped):
|
|
307
|
+
|
|
308
|
+
1. Record each with `dforge_module_plan({ action: "complete_phase", ... })`.
|
|
309
|
+
2. **Hand off:** invoke the **`dforge-module-ship`** skill for Phase 6.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dforge-module-design
|
|
3
|
+
description: Phase 0 of dForge module authoring — module identity, requirements intake, the design document, and pre-scaffold validation. Use when starting a NEW dForge module, or when dforge_module_plan reports a currentPhase of 0a/0b/0c/0d. Produces CLAUDE.md, docs/REQUIREMENTS.md, docs/DESIGN.md and docs/VALIDATION.md, and ends by unlocking dforge_module_create. Do not use for building entities or views — that is dforge-module-build.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dForge module — Design (Phase 0)
|
|
7
|
+
|
|
8
|
+
You are designing a module **before any code exists**. The output is four
|
|
9
|
+
documents. Nothing here creates entities, views, or JSON artifacts.
|
|
10
|
+
|
|
11
|
+
Phase 0 is driven by the **`dforge_module_plan`** tool: call it, ask what it
|
|
12
|
+
returns, write what it tells you to write. It is the source of truth for the
|
|
13
|
+
question lists, the design template, the gap checks, and the gate — don't
|
|
14
|
+
re-derive them from memory.
|
|
15
|
+
|
|
16
|
+
> **Prerequisite:** you should have arrived here from `dforge-mcp-author` after
|
|
17
|
+
> `dforge_module_plan({ action: "check", moduleDir })` reported a Phase 0
|
|
18
|
+
> `currentPhase`. If you haven't made that call yet, make it now.
|
|
19
|
+
|
|
20
|
+
## The loop
|
|
21
|
+
|
|
22
|
+
| Sub-phase | You do | Then call |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| **0a** Identity | Ask the 5 returned questions, ONE at a time | `write_identity` → write the returned `CLAUDE.md` to disk |
|
|
25
|
+
| **0b** Intake | Ask the returned questions in free-form prose; write `docs/REQUIREMENTS.md` to disk; get an explicit YES | `write_requirements { userConfirmed: true }` |
|
|
26
|
+
| **0c** Design | Draft `docs/DESIGN.md` from the returned `designTemplate` covering all 8 `designItems`; run the gap scan; write to disk; get an explicit YES | `write_design { userConfirmed: true }` |
|
|
27
|
+
| **0d** Validate | `validate` (structural) → evaluate the returned `semanticChecks` against the docs → `validate` again with `checkResults` | unlocks `readyToScaffold: true` |
|
|
28
|
+
|
|
29
|
+
After every user answer: **restate what you understood and confirm** before
|
|
30
|
+
moving on.
|
|
31
|
+
|
|
32
|
+
## Document-write ordering (the one exception to preview-first)
|
|
33
|
+
|
|
34
|
+
For `REQUIREMENTS.md` and `DESIGN.md` you **write the file to disk first**, then
|
|
35
|
+
ask the user to review the file and reply YES — do not paste the full document
|
|
36
|
+
into chat. Give them a short orientation instead: section headings, counts, and
|
|
37
|
+
gap status ("No gaps" or a one-line list).
|
|
38
|
+
|
|
39
|
+
On a change request, **edit the file directly** with targeted edits, summarize
|
|
40
|
+
the change in one line, and ask again. Loop until they confirm.
|
|
41
|
+
|
|
42
|
+
`userConfirmed: true` means the user actually said yes to the written file. The
|
|
43
|
+
tool has no way to check that — it is on you.
|
|
44
|
+
|
|
45
|
+
## 0b intake — guardrails the tool can't enforce
|
|
46
|
+
|
|
47
|
+
**Free-form prose only.** Ask every 0b question as a plain sentence. Do **NOT**
|
|
48
|
+
use `AskUserQuestion`, picker UIs, multiple-choice tabs, or any predefined-option
|
|
49
|
+
tool. Predefined buckets bias the answer into your taxonomy and destroy the verbs
|
|
50
|
+
Phase 5 needs to derive roles. Resume normal tool use in Phase 1.
|
|
51
|
+
|
|
52
|
+
Forbidden picker variants that have leaked before: _"Single role / Two roles /
|
|
53
|
+
Three+ roles"_; _"admin / manager / user / viewer"_.
|
|
54
|
+
|
|
55
|
+
**Capture user types as verb-form sentences, never role labels.** Write each as
|
|
56
|
+
`<descriptor of the person> <verb phrase>`:
|
|
57
|
+
|
|
58
|
+
✅ Good:
|
|
59
|
+
```
|
|
60
|
+
- Anyone in the company submits purchase requests and tracks their own.
|
|
61
|
+
- Department managers approve or reject pending requests for their team.
|
|
62
|
+
- Buyers in procurement manage suppliers, collect quotes, and issue purchase orders.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
❌ Bad (role-noun headings pre-commit Phase 5 to exactly those roles):
|
|
66
|
+
```
|
|
67
|
+
- **Requester** — submits purchase requests
|
|
68
|
+
- **Approver** — approves pending requests
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Push back on verb-less answers: "admins and users" → _"What does an admin do
|
|
72
|
+
that a user can't?"_
|
|
73
|
+
|
|
74
|
+
**Hard forbidden in 0b:** role codes (`<code>.admin`), role-noun bullet heads, a
|
|
75
|
+
rights matrix, or a "Target user roles" section. Roles are derived from entities
|
|
76
|
+
plus verbs in Phase 5 — and entities don't exist yet.
|
|
77
|
+
|
|
78
|
+
### Requirements gap scan
|
|
79
|
+
|
|
80
|
+
Run before writing `REQUIREMENTS.md`. Surface each finding inline as
|
|
81
|
+
**"Gap:** … **Proposal:** … Confirm or change?"**
|
|
82
|
+
|
|
83
|
+
- **Approval recovery** — is a reject terminal, or can it be re-submitted?
|
|
84
|
+
- **Audit depth** — does the user need to record *who* changed each row, not just
|
|
85
|
+
when? Only then `audit-full`. And if such an entity is also seeded, every seed
|
|
86
|
+
record must set `created_by`/`last_updated_by: 0` (the System user) — otherwise
|
|
87
|
+
use plain `audit`.
|
|
88
|
+
- **Integration codes** — the exact `module.entity` codes for anything external.
|
|
89
|
+
- **Implied-but-unnamed entities.**
|
|
90
|
+
- **Reference-number scale** → does it need a number sequence?
|
|
91
|
+
|
|
92
|
+
## 0c design — the gap detection pass
|
|
93
|
+
|
|
94
|
+
After drafting, run the gap detection the tool lists, and add a **Gaps &
|
|
95
|
+
Proposals** section for each finding. **All gaps must be resolved before 0d.**
|
|
96
|
+
The scan covers: FK optionality, status recovery paths, boolean-that-should-be-a-
|
|
97
|
+
status, set aggregation risk, deep navigation (2+ FK hops), self-referential FKs,
|
|
98
|
+
security coverage, and seed-data circular references.
|
|
99
|
+
|
|
100
|
+
Two design decisions to get right here, because fixing them in Phase 1 is a
|
|
101
|
+
backtrack:
|
|
102
|
+
|
|
103
|
+
- **A total over child rows is a Generated column**, not a Formula. Note it as
|
|
104
|
+
such in the design. (`dforge_entity_rollup_add` enforces it later.)
|
|
105
|
+
- **A field with 3+ discrete states is a status**, not a boolean — and a 3+ value
|
|
106
|
+
status is the objective trigger for a kanban view in Phase 3.
|
|
107
|
+
|
|
108
|
+
## 0d validation — what it does and doesn't certify
|
|
109
|
+
|
|
110
|
+
`docs/VALIDATION.md` / `readyToScaffold: true` certifies only that the **design
|
|
111
|
+
documents are internally consistent**. It runs *before* scaffolding and inspects
|
|
112
|
+
no entity / UI / security / DSL file.
|
|
113
|
+
|
|
114
|
+
Artifact correctness is enforced by `dforge_module_validate` and by the platform
|
|
115
|
+
at install. **A green VALIDATION.md is not a signal that the module will
|
|
116
|
+
install.** Say so if the user reads it that way.
|
|
117
|
+
|
|
118
|
+
## Exit
|
|
119
|
+
|
|
120
|
+
When `validate` returns `readyToScaffold: true`:
|
|
121
|
+
|
|
122
|
+
1. Write the returned `CLAUDE.md`, `docs/VALIDATION.md` and `docs/phase.json`.
|
|
123
|
+
2. Tell the user Phase 0 is complete and summarize the design in two lines.
|
|
124
|
+
3. **Hand off:** invoke the **`dforge-module-build`** skill to scaffold and build
|
|
125
|
+
Phases 1–5. Do not call `dforge_module_create` from this skill.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dforge-module-ship
|
|
3
|
+
description: Phase 6 of dForge module authoring — the verify-and-release stage. Runs dforge_module_validate, the pre-pack self-review checklist, the version audit, then dforge_module_pack and dforge_module_install, and drives the install-fix loop until the module installs against a real tenant. Use when dforge_module_plan reports currentPhase 6, or when the user asks to validate, pack, install, deploy, or fix a failing install of an existing dForge module.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dForge module — Ship (Phase 6)
|
|
7
|
+
|
|
8
|
+
Phase 6 is **required and non-skippable**. Its job is to find every defect
|
|
9
|
+
offline, then get a clean install against a real tenant.
|
|
10
|
+
|
|
11
|
+
**Preconditions:** Phases 0a–0d, 1, 3a and 5a complete. Optional phases (2, 4,
|
|
12
|
+
3b/3c, 5b) don't block — explicitly skipped ones are fine.
|
|
13
|
+
|
|
14
|
+
> **The platform validator at install is the only complete validator.** Every
|
|
15
|
+
> step before it exists to make that round trip succeed on the first or second
|
|
16
|
+
> try, because it is slow and tenant-bound.
|
|
17
|
+
|
|
18
|
+
## Step 1 — Automated validation (blocking gate)
|
|
19
|
+
|
|
20
|
+
Call **`dforge_module_validate`** on the module dir. It runs offline what
|
|
21
|
+
otherwise only surfaces at install:
|
|
22
|
+
|
|
23
|
+
- dangling FK / reference targets; a missing hidden-FK column
|
|
24
|
+
- view `dataSources`/columns pointing at unknown entities/fields; a grid over an
|
|
25
|
+
entity with no visible column
|
|
26
|
+
- menu `dataViewCode` → missing view; role rights on unknown objects
|
|
27
|
+
- entities with no Select grant
|
|
28
|
+
- **field-spec rules re-run across every field** — catching anything that entered
|
|
29
|
+
via `module_import` / `dbml_import` / a hand edit and never passed the
|
|
30
|
+
authoring schema
|
|
31
|
+
- `toString` templates whose braces don't resolve
|
|
32
|
+
- **Formula-vs-Generated set aggregates**, and Generated aggregates over a
|
|
33
|
+
virtual child column
|
|
34
|
+
- action `script` files missing from disk; triggers/jobs firing actions that
|
|
35
|
+
don't exist; `[field]` record-context in a job-invoked action
|
|
36
|
+
- **DSL static checks** on every action body
|
|
37
|
+
- **translation completeness** — a missing `roles.<code>.label` fails install
|
|
38
|
+
|
|
39
|
+
Read `files["_validate.json"]`. **Every `error` must be fixed** (apply the
|
|
40
|
+
backtrack protocol in `dforge-module-build`) before continuing. Review the
|
|
41
|
+
`warning`s with the user. Re-run `dforge_module_validate` until `ok: true`.
|
|
42
|
+
|
|
43
|
+
If `dforge_module_validate` returns a tool error or is unavailable, halt and
|
|
44
|
+
tell the user: the automated gate cannot be bypassed; resolve the tooling issue
|
|
45
|
+
before continuing.
|
|
46
|
+
|
|
47
|
+
Structural only — it doesn't judge intent, so still do Step 2.
|
|
48
|
+
|
|
49
|
+
## Step 2 — Pre-pack self-review (blocking gate)
|
|
50
|
+
|
|
51
|
+
Load `dforge://reference/validation-checklist` and work every section.
|
|
52
|
+
|
|
53
|
+
**Top install-blockers — scan these first.** Each is a documented real failure
|
|
54
|
+
the platform validator rejects:
|
|
55
|
+
|
|
56
|
+
1. **DSL dates** — `execute:` uses lowercase `now()`; never `TODAY()`/`NOW()`
|
|
57
|
+
(formula-only → `'TODAY' is not defined`). Verify with
|
|
58
|
+
`dforge_action_check({ moduleDir, actionCode })` per action.
|
|
59
|
+
2. **Roll-ups** — a total over a child set is a Generated (`G`) column with
|
|
60
|
+
`SUM([set].[field])`, never a Formula (`F`), and it aggregates a **physical**
|
|
61
|
+
child column (`D` or same-row `G`). A virtual `F`/`R`/`S` child fails with
|
|
62
|
+
`column old.<field> does not exist`.
|
|
63
|
+
3. **Rights keys** — actions/reports/folders take a **colon** (`action:x`);
|
|
64
|
+
entities are bare or cross-module-dotted; deny by omitting, never `""`.
|
|
65
|
+
4. **Manifest** — no `translations` key; non-English locales in
|
|
66
|
+
`supportedLocales` (never `en`/`en-US`); `moduleId` a valid UUID; `version`
|
|
67
|
+
and `dbSchemaVersion` set; `security` has both `roles` and `folders`.
|
|
68
|
+
5. **Column defaults** — via `formula` / `numberSequence` / DSL, never a
|
|
69
|
+
`defaultValue` key on an entity field.
|
|
70
|
+
6. **Seed + traits** — a seeded `audit-full` entity sets
|
|
71
|
+
`created_by`/`last_updated_by: 0` on every record, or uses plain `audit`.
|
|
72
|
+
|
|
73
|
+
Remaining checklist areas: entity traits + `toString` + the FK+Reference pattern;
|
|
74
|
+
formula column shape; flags letters (`V I E M H` only); every entity has a
|
|
75
|
+
default grid and `dataSources` at root with `"order"` as a string array; menu
|
|
76
|
+
leaves use `dataViewCode` and bare icon names; every entity in at least one
|
|
77
|
+
role's rights; action `script` values are bare filenames; seed PKs numeric with
|
|
78
|
+
parents first; a `translations/<locale>.json` for every `supportedLocales` entry
|
|
79
|
+
**plus** the `en-US` base, each carrying a label for every trait-provided field
|
|
80
|
+
and every role.
|
|
81
|
+
|
|
82
|
+
If translations are thin, run **`dforge_translation_sync`** now rather than
|
|
83
|
+
hand-patching — it fills exactly the keys install requires.
|
|
84
|
+
|
|
85
|
+
## Step 3 — Translation deferral check
|
|
86
|
+
|
|
87
|
+
Read `_brief/changelog.md`. If a translation deferral warning is present
|
|
88
|
+
("Translation files for [locales] are incomplete"), **halt**: install will fail
|
|
89
|
+
completeness validation. Run `dforge_translation_sync`, confirm the non-English
|
|
90
|
+
values are really translated (the tool seeds English placeholders), and clear the
|
|
91
|
+
warning before proceeding.
|
|
92
|
+
|
|
93
|
+
## Step 4 — Final inspect + version audit
|
|
94
|
+
|
|
95
|
+
Run `dforge_module_inspect`. Show a one-line summary — entity / view / action /
|
|
96
|
+
role / trigger / job counts. Then confirm both version strings with the user:
|
|
97
|
+
|
|
98
|
+
- **`version`** — always bump (semver) before packing.
|
|
99
|
+
- **`dbSchemaVersion`** — bump only if entity fields were added, removed, or
|
|
100
|
+
type-changed since the last install.
|
|
101
|
+
|
|
102
|
+
Get explicit confirmation on both before packing. This version confirmation
|
|
103
|
+
applies only to the initial pack. During the install-fix loop, re-pack without
|
|
104
|
+
re-asking unless the fix changes the version strings.
|
|
105
|
+
|
|
106
|
+
## Step 5 — Pack + install
|
|
107
|
+
|
|
108
|
+
1. `dforge_module_pack` → the `.dforge` tarball. Blocked if any entity lacks a
|
|
109
|
+
role granting Select; fix security coverage and re-run.
|
|
110
|
+
2. `dforge_module_install` with `DFORGE_URL` / `DFORGE_TOKEN`.
|
|
111
|
+
|
|
112
|
+
### Install-fix loop (mandatory)
|
|
113
|
+
|
|
114
|
+
1. **Call `dforge_module_install` yourself.** Never ask the user to run the
|
|
115
|
+
install command for you.
|
|
116
|
+
2. If `ok: false`, read the returned `output` **in full** — it's the raw
|
|
117
|
+
CLI/server validator output and it is the input to the next fix.
|
|
118
|
+
3. If it's a module defect, **fix it yourself.** Use the table below to pick the
|
|
119
|
+
phase, then apply the backtrack protocol with the smallest suitable tool.
|
|
120
|
+
4. Re-run Step 1 (`validate`) until `ok: true`, then re-run only the Step 2
|
|
121
|
+
checks for the area you touched, then pack and install again.
|
|
122
|
+
5. Repeat until install succeeds, or the remaining failure is clearly
|
|
123
|
+
environment/tooling.
|
|
124
|
+
|
|
125
|
+
Environment/tooling failures include: 401/403 auth errors, network connectivity
|
|
126
|
+
refused, server-side feature not enabled for the tenant, and CLI version
|
|
127
|
+
incompatibility. All other errors are module defects.
|
|
128
|
+
|
|
129
|
+
After 4 failed install attempts without progress, pause the loop, summarize all
|
|
130
|
+
outstanding errors to the user, and ask how to proceed rather than continuing
|
|
131
|
+
autonomously.
|
|
132
|
+
|
|
133
|
+
| Install error pattern | Backtrack to |
|
|
134
|
+
|---|---|
|
|
135
|
+
| "unknown entity code" / "unknown view code" | Phase 1 or 3 |
|
|
136
|
+
| "missing translation key" | Phase 4 (`dforge_translation_sync`) |
|
|
137
|
+
| "FK constraint violation in seed data" | Phase 1 (seed load order) |
|
|
138
|
+
| "role right granted on unknown object" | Phase 5 |
|
|
139
|
+
| "action script not found" | Phase 2a |
|
|
140
|
+
| "formula compile error" | Phase 1 (field def) or Phase 2a (DSL) |
|
|
141
|
+
| "duplicate code" | wherever the duplicate was introduced |
|
|
142
|
+
|
|
143
|
+
**Phase 6 sign-off exception:** when install fails with a clear module defect
|
|
144
|
+
(schema validation, missing file/key, translation completeness, DSL compile
|
|
145
|
+
error, dependency contract, FK/seed/import error), do **not** ask the user for
|
|
146
|
+
sign-off before the corrective patch. The server has already rejected the
|
|
147
|
+
package — fix the referenced files, report what changed, and immediately re-run
|
|
148
|
+
validate → pack → install. Keep sign-off for product/design choices or genuinely
|
|
149
|
+
ambiguous fixes.
|
|
150
|
+
|
|
151
|
+
**If install fails on auth (401/403) or connectivity (refused):** that is
|
|
152
|
+
credentials/connectivity, NOT a module defect. Tell the user to verify
|
|
153
|
+
`DFORGE_URL` and `DFORGE_TOKEN`. **Do not backtrack** to earlier phases.
|
|
154
|
+
|
|
155
|
+
**Exit criterion:** install exits 0 against a real tenant.
|
|
156
|
+
|
|
157
|
+
## Step 6 — Record and close out
|
|
158
|
+
|
|
159
|
+
1. `dforge_module_plan({ action: "complete_phase", moduleDir, phase: "6", note: "installed v<x.y.z>" })`.
|
|
160
|
+
2. **Ask the user**: "Delete `_brief/` (session scratch), or move it to `docs/`
|
|
161
|
+
as committed design rationale?" Wait for the answer — don't act unilaterally.
|
|
162
|
+
3. Suggest a `git commit` summarizing the module. **Do not commit unless the user
|
|
163
|
+
asks.**
|