@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
|
@@ -1,488 +1,164 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dforge-mcp-author
|
|
3
|
-
description:
|
|
3
|
+
description: Entry point for authoring dForge modules with the dforge-mcp tool surface. Use when @dforge-core/dforge-mcp is connected and the user asks to create, extend, fix, pack, or install a dForge module. Determines where the module currently stands and hands off to the right stage skill — dforge-module-design (Phase 0), dforge-module-build (Phases 1-5), or dforge-module-ship (Phase 6). Start here when you don't already know which stage you're in.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# dForge Module Author —
|
|
6
|
+
# dForge Module Author — router
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
This skill routes. It does **not** contain the phase instructions — those live in
|
|
9
|
+
three stage skills, each loaded only when its stage is active:
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
**Your first action in every new or resumed module session is to call `dforge_module_plan({ action: "check", moduleDir })`.** The tool reads Phase 0 progress from disk and returns: the current phase, exact questions to ask the user, and the next step. Follow its instructions.
|
|
13
|
-
|
|
14
|
-
- **If the user hasn't specified `moduleDir` yet**, ask for it before calling the tool: "Where should the module directory live? (absolute path)"
|
|
15
|
-
- **If the user asks to skip Phase 0** ("just scaffold it", "skip the docs", "I don't need requirements", or any equivalent): respond with — _"Phase 0 documents are required before scaffolding. They take 15–30 minutes and prevent hours of backtracking. Let me check where we are."_ — then call `dforge_module_plan({ action: "check", moduleDir })` immediately.
|
|
16
|
-
- **`dforge_module_create` is gated at the tool level.** It throws if `docs/VALIDATION.md` is missing or doesn't show a clean pass. Do not attempt to bypass the gate.
|
|
17
|
-
|
|
18
|
-
## Tool reference
|
|
19
|
-
|
|
20
|
-
The phase column below indicates the **typical** use. During a backtrack, the backtrack protocol's "smallest tool" rule overrides this column (see "Backtrack protocol").
|
|
21
|
-
|
|
22
|
-
| Tool | Typical phase | What it does |
|
|
23
|
-
|---|---|---|
|
|
24
|
-
| `dforge_module_plan` | 0 | **Phase 0 orchestrator — call first.** Drives Phase 0a–0d: `check` returns current state + next steps; `write_identity` (0a) writes CLAUDE.md; `write_requirements` (0b) confirms REQUIREMENTS.md (which you write to disk yourself) after user YES and ticks CLAUDE.md; `write_design` (0c) confirms DESIGN.md (which you write to disk yourself) after user YES and ticks CLAUDE.md; `validate` (0d) runs checks + writes VALIDATION.md. `readyToScaffold: true` unlocks `dforge_module_create`. |
|
|
25
|
-
| `dforge_module_inspect` | any | Read current module state. **Read-only** — output does NOT require user confirmation. The one-line `summary` is for the user; the full structured state lives in `files["_inspect.json"]` (entities + their fields, views + their data sources, roles + rights matrix, actions, reports, settings, folders tree). Parse `_inspect.json` before planning patches — don't rely on summary text alone. |
|
|
26
|
-
| `dforge_module_validate` | 6 (or any) | **Read-only** offline cross-reference check. Catches dangling FK/reference targets, a missing hidden-FK column, view/menu/role references to things that don't exist, and uncovered entities — errors that otherwise only surface at install. Read `files["_validate.json"]`; fix every `error` before packing. Run it as the first Phase 6 step, and after any backtrack that touched references. |
|
|
27
|
-
| `dforge_module_create` | 1 | Scaffold a new module — **blocked until Phase 0d passes** (all four Phase 0 docs written + validated) |
|
|
28
|
-
| `dforge_module_import` | 1 | Import a normalized **table-spec** (tables → columns → relationships) into an existing module as entities. Infers each column's `fieldTypeCd` (from explicit code / SQL type / sample values / name) and builds the FK+Reference pair per relationship. Front-end is DBML/SQL, Excel/CSV, or hand-authored. ADDS entities; review inferred types + refine views, then validate. |
|
|
29
|
-
| `dforge_entity_add` | 1 | Add a whole entity to an existing module |
|
|
30
|
-
| `dforge_entity_rename` | backtrack | **Refactor-safe entity rename.** Moves the file (apply the response `deletes`), cascades the identity PK `{old}_id → {new}_id`, and repoints link.entity / references / view entityCode / role keys / action entity / folder bindings / seed. Reports/translations/menus/DSL warned, not rewritten. |
|
|
31
|
-
| `dforge_entity_delete` | backtrack | **Refactor-safe entity delete.** Drops the file + seed (in `deletes`), manifest entry, role key, folder binding, and view sources (deletes a view left empty). Cross-entity FKs / actions / menus warned. |
|
|
32
|
-
| `dforge_entity_field_add` | 1 | Patch one field onto an existing entity |
|
|
33
|
-
| `dforge_entity_field_modify` | 1 | Replace one field's spec (same name) |
|
|
34
|
-
| `dforge_entity_field_rename` | 1 / backtrack | **Refactor-safe rename.** Propagates the new name to the paired Reference (`link.thisKey` + `references`), same-entity formulas, view columns + `order`, seed-data records, and other entities' FKs targeting it. Use this to rename a field — never remove+add. |
|
|
35
|
-
| `dforge_entity_field_remove` | 1 / backtrack | **Refactor-safe remove.** Drops the field and cascade-cleans the paired Reference (when removing its FK), references entry, view columns + `order`, and seed keys. Formula/cross-entity dependents are warned, not auto-deleted. |
|
|
36
|
-
| `dforge_action_add` | 2 | DSL action + ui/actions.json entry |
|
|
37
|
-
| `dforge_trigger_add` | 2 | DB-event trigger in logic/triggers.json |
|
|
38
|
-
| `dforge_job_add` | 2 | Scheduled job in logic/jobs.json |
|
|
39
|
-
| `dforge_webhook_add` | 2 | Outbound webhook in logic/webhooks.json |
|
|
40
|
-
| `dforge_view_add` | 3 | Data view in ui/data_views.json |
|
|
41
|
-
| `dforge_view_modify` | 3 | Replace a view's spec |
|
|
42
|
-
| `dforge_report_add` | 3 | Report in ui/reports.json |
|
|
43
|
-
| `dforge_setting_add` | 4 | Configurable module setting |
|
|
44
|
-
| `dforge_role_add` | 5 | Role with rights matrix |
|
|
45
|
-
| `dforge_role_right_set` | 5 | Grant / revoke one right on one object |
|
|
46
|
-
| `dforge_folder_add` | 5 | Security folder (optional) |
|
|
47
|
-
| `dforge_dependency_add` | any | Add a dep on another module |
|
|
48
|
-
| `dforge_module_pack` | 6 | Produce .dforge tarball via bundled dforge-cli, PATH fallback, or `DFORGE_CLI_BINARY` |
|
|
49
|
-
| `dforge_module_install` | 6 | Install to tenant — the real validator; returns raw CLI output for the install-fix loop |
|
|
50
|
-
|
|
51
|
-
**Phase 0 (0a–0d) is owned by `dforge_module_plan`.** Call `dforge_module_plan({ action: "check", moduleDir })` to start or resume Phase 0. The tool returns the current state and exact next steps. Do not call `dforge_module_create` until the tool reports `readyToScaffold: true` — the tool enforces this gate programmatically.
|
|
52
|
-
|
|
53
|
-
## Loading policy — lazy, reference-first
|
|
54
|
-
|
|
55
|
-
**Load nothing up front beyond this skill.** There is no session-start resource load. Pull material only at the step that needs it, and only what that step requires — this is what keeps a full scaffold inside one context window.
|
|
56
|
-
|
|
57
|
-
- **References and examples are MCP resources — load them by URI, not by filesystem path.** Each per-element reference is `dforge://reference/<name>` and each example file is `dforge://example/<path>` (e.g. `dforge://reference/flags`, `dforge://example/entities/todo_item.json`). These resolve from the server regardless of your working directory — do NOT try to `Read` a `references/*.md` path off disk; your CWD is the module dir, not the skill dir, so that path won't exist. Use the resource.
|
|
58
|
-
- **One reference per step.** The `dforge://reference/<name>` in the table below is your primary source — it carries the schema shape, a worked example, and the common-mistakes list. Load it (plus the `dforge://example/...` file in the same row) before authoring that element type, and re-load it on every backtrack into that type. The example files are mandatory structure validators; never work from memory for schema shapes, flags, or column-type patterns.
|
|
59
|
-
- **Schemas are fallback, not default.** Load a `dforge://schema/*` (or `dforge://docs/dsl`) resource only when the **Also load** column names one, or when a reference explicitly points you to its schema for a shape it doesn't fully specify.
|
|
60
|
-
- **`dforge://docs/conventions` is not loaded by default** — per-element references cover the same ground. Load it only for cross-module **extension / bridge** work (its §1b is the one topic with no dedicated reference).
|
|
61
|
-
- **Halt per load.** If a reference or schema you actually need fails to load, stop and notify the user — do not invent conventions or schemas from memory.
|
|
62
|
-
|
|
63
|
-
| When you need to… | Load (primary reference + example) | Also load (schema / doc) |
|
|
11
|
+
| Stage skill | Phases | What it owns |
|
|
64
12
|
|---|---|---|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
1
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
- **Flags** = letters from `V I E M H` only (no `U`/`S`/`P`): `VEM` required+visible, `VE` optional+visible, `V` read-only, `EM` hidden FK, `I` trait-provided. → `flags.md`
|
|
114
|
-
- **Field types:** `fieldTypeCd` = UI control, `dbDatatype` = SQL type. **Omit `dbDatatype` on plain data columns — it's derived from `fieldTypeCd`** (`currency` → `numeric(18,2)`, `text` → `varchar`). Only set it for a **hidden FK** (no `fieldTypeCd`, so use the target PK type `cuid`) or to override size/precision. When you do set it: never the same as `fieldTypeCd`, and never `"number"` (use `int`/`bigint`/`numeric`). Common `fieldTypeCd` fixes: `number` not `integer`/`float`, `phone` not `phoneNumber`, `date` not `datePicker`. Common `dbDatatype` fixes: `timestamptz` not `datetime`/`timestamp`, `bool` not `boolean`, `varchar`/`text` not `string`. (Invalid `fieldTypeCd`/`columnType` are now rejected at authoring time.) → `field-types.md`
|
|
115
|
-
- **Formula columns** (`columnType: "F"`): `baseDatatypeCd` required, no `dbDatatype`, `flags: "V"`. → `formulas.md`
|
|
116
|
-
- **Roll-up totals** over a child set → Generated `G` column with `SUM([set].[field])` (`dbDatatype` + `formula`, no `link`/`baseDatatypeCd`); the installer maintains it with a DB trigger. **Not** a Formula `F` — an `F` set-aggregate is unsupported and silently renders empty. Aggregate only a **physical** child column (`D` or same-row `G`); a virtual `F`/`R`/`S` child fails install (`column old.<field> does not exist`). → `column-types.md`
|
|
117
|
-
- **Column defaults:** entity *data* columns have **no** `defaultValue`/`default` key. Set a default via a formula (`"formula": "TODAY()"`, `"formula": "'draft'"`) or in action/trigger logic. `defaultValue` is **settings-only**. → `field-types.md`
|
|
118
|
-
- **Traits:** default `["identity", "audit"]`; `audit-full` (when the user needs *who*-tracking) adds required `created_by`/`last_updated_by` with no default — if such an entity is **seeded**, set both to the System user `0` in every record, or don't seed it. → `traits.md`
|
|
119
|
-
- **`toString`:** every entity needs one, `{column}` braces, e.g. `"{first_name} {last_name}"`.
|
|
120
|
-
- **Data views:** `dataSources` array at root — never root-level `entityCode` + `columns`. → `data-views.md`
|
|
121
|
-
- **Menus:** leaf items use `dataViewCode` (not `viewCode`); section nodes omit `itemType`; icons are Bootstrap names sans `bi-`. → `menus.md`
|
|
122
|
-
- **Security roles:** `rights` (not `entityRights`); entities `SIUDC`, actions/reports/folders `E`. Rights keys: same-module entity bare (`product`), cross-module entity dotted (`fin.invoice`), and actions/reports/folders use a **colon** prefix — `action:approve`, `report:summary`, `folder:east` (never a dot). Omit a key to deny; never map to `""`. `roles.json` carries `description` (English fallback) + `rights` only — **no `label`**; the localized role display name lives in the translation files as `roles.<code>.label` and is completeness-enforced. → `security.md`, `translations.md`
|
|
123
|
-
- **Action script** in `ui/actions.json` = bare filename (no path, no `.dsl`).
|
|
124
|
-
- **Action DSL dates:** inside `execute:` use lowercase `now()`; `TODAY()`/`NOW()` are formula-only (`canExecute:`/formula columns) and are **undefined in `execute:`**. → `action-dsl.md`
|
|
125
|
-
- **SQL placeholders** = `@paramName` (not `:paramName`).
|
|
126
|
-
- **Manifest:** non-English locales go in `supportedLocales` (array of `ll-CC` tags; never `en`/`en-US`) — there is no `translations` manifest key; files are auto-discovered at `translations/<locale>.json`. `security` has both `roles` and `folders`. → `manifest.md`
|
|
127
|
-
- **Seed data:** explicit numeric PKs (`cuid` is `int8`, not a UUID); parents before children via numeric prefix (`01-`, `02-`).
|
|
128
|
-
|
|
129
|
-
## Tool failure protocol
|
|
130
|
-
|
|
131
|
-
If any MCP tool returns an error at any time:
|
|
132
|
-
|
|
133
|
-
1. **Surface the raw error verbatim** to the user. Do not paraphrase.
|
|
134
|
-
2. **Classify the error before asking the user for help.** If the raw output names a module/package defect, fix the module yourself using the smallest appropriate tool or direct file edit allowed by the host agent, then re-run the required validation loop. Do not ask the user to paste the error back to you — it is already in the tool result.
|
|
135
|
-
3. **Ask the user to resolve only environment/tooling issues** you cannot fix from module files: missing CLI, missing/expired credentials, unreachable tenant/API, permissions, or a bad module path outside the workspace.
|
|
136
|
-
4. **Do not advance the phase** until the failing tool succeeds.
|
|
137
|
-
|
|
138
|
-
Two specific tool errors have known causes worth distinguishing:
|
|
139
|
-
|
|
140
|
-
- **`dforge_module_pack` / `_install` reports "command not found" or PATH error**: dforge-cli isn't installed. Tell the user: "dforge-cli is not on your PATH. Install with `npm install -g @dforge-core/dforge-cli`, then re-run. Do not continue Phase 6 until resolved."
|
|
141
|
-
- **`dforge_module_install` reports HTTP 401/403 or connection refused**: this is auth/connectivity, NOT a module defect. Tell the user: "This appears to be a credentials or connectivity issue, not a module defect. Verify `DFORGE_URL` and `DFORGE_TOKEN` before re-running install." Do not backtrack to earlier phases.
|
|
142
|
-
- **`dforge_module_install` returns `ok: false` with validation/import/compile/schema output**: this is a module defect. Treat `output` as the source of truth, fix the referenced files, and run Phase 6 again from automated validation. Keep repeating until install succeeds or the remaining error is clearly environment/tooling.
|
|
143
|
-
|
|
144
|
-
## Resume-from-partial-state
|
|
145
|
-
|
|
146
|
-
At every session start, **inspect** the module: call `dforge_module_inspect` on the module dir (if the user has specified one). Loading is otherwise lazy (see Loading policy) — do **not** bulk-load resources or schemas up front.
|
|
147
|
-
|
|
148
|
-
Phase 0 progress is tracked by **which artifact files exist on disk**. Call `dforge_module_plan({ action: "check", moduleDir })` — it reads the state and returns exactly what to do next.
|
|
149
|
-
|
|
150
|
-
- If the dir doesn't exist or has no `manifest.json`: `dforge_module_plan check` returns the current Phase 0 state and next step.
|
|
151
|
-
- If the dir does exist (manifest found):
|
|
152
|
-
1. Read `_brief/changelog.md` if present.
|
|
153
|
-
2. Call `dforge_module_inspect` to get entity/view/role inventory.
|
|
154
|
-
3. Cross-reference entities/views/roles to infer last completed phase.
|
|
155
|
-
4. Summarize: "Found module `<code>` v`<version>`. Looks like Phase N was the last completed phase. Resume from Phase N+1, or revisit an earlier phase?"
|
|
156
|
-
5. Wait for the user's answer before proceeding.
|
|
157
|
-
|
|
158
|
-
## Phase 0 — owned by `dforge_module_plan`
|
|
159
|
-
|
|
160
|
-
Phase 0 (0a identity → 0b intake → 0c design → 0d validation) is driven entirely by the tool. **Call `dforge_module_plan({ action: "check", moduleDir })` and follow the fields it returns** — `questions` (ask ONE at a time), `designItems`, `designTemplate`, `gapDetection`, `semanticChecks`, and `nextStep`/`writeAction`. The tool is the source of truth for those lists; don't re-derive or re-enumerate them here. After every user answer, apply the validate-and-reflect rule (hard rule #4).
|
|
161
|
-
|
|
162
|
-
The loop the tool walks you through:
|
|
163
|
-
|
|
164
|
-
| Sub-phase | You do | Then call |
|
|
165
|
-
|---|---|---|
|
|
166
|
-
| 0a Identity | ask the 5 returned questions one at a time | `write_identity` → write the returned `CLAUDE.md` to disk |
|
|
167
|
-
| 0b Intake | ask the returned questions (free-form prose — see guardrails below); write `docs/REQUIREMENTS.md` to disk; get explicit YES | `write_requirements { userConfirmed: true }` |
|
|
168
|
-
| 0c Design | draft `docs/DESIGN.md` from the returned `designTemplate`, covering the 8 `designItems`; run the gap detection the tool lists; write to disk; get explicit YES | `write_design { userConfirmed: true }` |
|
|
169
|
-
| 0d Validate | `validate` (structural) → evaluate the returned `semanticChecks` against the docs (read them from disk) → `validate` again with `checkResults` | unlocks `readyToScaffold: true` |
|
|
170
|
-
|
|
171
|
-
**Document-write ordering (exception to hard rule #1):** for REQUIREMENTS.md and DESIGN.md you write the file to disk *first*, then ask the user to review it and reply YES — do not paste the full document into chat. On change requests, edit the file directly (targeted edits) and re-ask until confirmed. `dforge_module_create` stays gated at the tool level until `readyToScaffold: true`.
|
|
172
|
-
|
|
173
|
-
> **What Phase 0d validates — and what it doesn't.** `docs/VALIDATION.md` / `readyToScaffold: true` certifies only that the **design documents** are internally consistent. It runs *before* scaffolding and does **not** inspect any generated entity / UI / security / DSL file. Artifact correctness is enforced by the **platform at install (Phase 6)** — a green VALIDATION.md is not a signal that the module will install. The Phase 6 automated validation + pre-pack self-review (Steps 1–2) are your real safeguard.
|
|
174
|
-
|
|
175
|
-
### 0b intake — guardrails the tool can't enforce
|
|
176
|
-
|
|
177
|
-
**Free-form prose only — no pickers.** Ask every 0b question as a plain sentence. Do **NOT** use `AskUserQuestion`, picker UIs, multiple-choice tabs, or any predefined-option tool — predefined buckets bias the answer into your taxonomy and lose the verbs Phase 5 needs. Resume normal tool use in Phase 1+. Forbidden picker variants that have leaked before: "Single role / Two roles / Three+ roles"; "admin / manager / user / viewer".
|
|
178
|
-
|
|
179
|
-
**Capture user types as verb-form sentences, never role labels.** Write each as `<descriptor of the person> <verb phrase>`:
|
|
180
|
-
|
|
181
|
-
✅ Good:
|
|
182
|
-
```
|
|
183
|
-
- Anyone in the company submits purchase requests and tracks their own.
|
|
184
|
-
- Department managers approve or reject pending requests for their team.
|
|
185
|
-
- Buyers in procurement manage suppliers, collect quotes, and issue purchase orders.
|
|
186
|
-
```
|
|
187
|
-
❌ Bad (role-noun headings bias Phase 5 toward exactly those roles):
|
|
188
|
-
```
|
|
189
|
-
- **Requester** — submits purchase requests
|
|
190
|
-
- **Approver** — approves pending requests
|
|
191
|
-
```
|
|
192
|
-
Push back on verb-less answers ("admins and users" → "What does an admin do that a user can't?"). **Hard forbidden in 0b:** role codes (`<code>.admin`), role-noun bullet heads, a rights matrix, or a "Target user roles" section — roles are derived from entities + verbs in Phase 5, and entities don't exist yet.
|
|
193
|
-
|
|
194
|
-
**Requirements gap scan** (run before writing REQUIREMENTS.md; surface inline as "**Gap:** … **Proposal:** … Confirm or change?"): approval recovery (is reject terminal or re-submittable?); audit depth (does the user need to record *who* changed each row, not just when? only then `audit-full`; if that entity is also seeded, each seed record must set `created_by`/`last_updated_by: 0`, the System user — otherwise `audit`); exact `module.entity` codes for any integration; implied-but-unnamed entities; reference-number scale → sequence pattern.
|
|
195
|
-
|
|
196
|
-
## Phase 1 — Domain (required)
|
|
197
|
-
|
|
198
|
-
> **Fast on-ramp — importing from an existing schema or spreadsheet.** When the user already has the data model in a **DBML** diagram or a **spreadsheet** (Excel/CSV), use the import core instead of authoring entities one field at a time:
|
|
199
|
-
> - **DBML/SQL** → `dforge_dbml_import` (deterministic parser). Pass the DBML text and, for a brand-new module, `module: { code, displayName }`.
|
|
200
|
-
> - **Spreadsheet (.xlsx/.csv)** → load `dforge://reference/excel-import` and follow it: a binary `.xlsx` can't be read directly, so first **decode it** with the bundled stdlib Python extractor (`dforge://script/xlsx-to-model` → write to a temp file → `python3 tmp.py file.xlsx`), which prints `{sheets:[{name,headers,rows}]}`. Then turn that model into a table-spec — each sheet → one table, headers → columns, rows → `sampleValues`, `<thing>_id` columns → `references` — and call `dforge_module_import({ moduleDir, tables, module? })`. (A `.csv` is plain text: read it directly, no extractor.)
|
|
201
|
-
> Both infer `fieldTypeCd` from the metadata registry and build the FK+Reference pair. **Always show the proposed table-spec / entity inventory to the user for sign-off first** (same gate as below), then **run `dforge_module_validate`** and refine the generated default grids. Import ADDS entities into an existing or greenfield module; it does not replace Phase 0 for a from-scratch design the user wants to think through.
|
|
202
|
-
|
|
203
|
-
**Preconditions:** Phases 0a through 0d complete — `CLAUDE.md` written; `docs/REQUIREMENTS.md` confirmed; `docs/DESIGN.md` confirmed; `docs/VALIDATION.md` shows a clean pass with no open findings.
|
|
204
|
-
|
|
205
|
-
> ⛔ **GATE — `dforge_module_create` is blocked at the tool level.** It throws if any of the four Phase 0 docs are missing or if `docs/VALIDATION.md` doesn't contain `readyToScaffold: true`. If you hit the gate error, call `dforge_module_plan({ action: "check", moduleDir })` to see what's needed.
|
|
206
|
-
|
|
207
|
-
**This phase's FIRST deliverable — before any tool call — is the proposed entity inventory.** Show it. Get explicit sign-off. Then scaffold. The user needs to see "the module will have these N things in it" before files exist, because entities are the spine the rest of the module hangs from (views, actions, roles all reference entity codes).
|
|
208
|
-
|
|
209
|
-
**Pre-scaffold validation** — before calling `dforge_module_create`, run these five consistency checks against `docs/DESIGN.md`. If any fail, surface the issue to the user and return to Phase 0c to fix it — do not silently adjust the design:
|
|
210
|
-
|
|
211
|
-
1. Every FK in the relationship map has a corresponding field listed for the child entity.
|
|
212
|
-
2. Every action's `canExecute` guard references a status value that exists in that entity's options list.
|
|
213
|
-
3. Every seed record's FK references a parent entity that also has seed data (referential integrity in load order).
|
|
214
|
-
4. Every formula column uses only fields that exist on the same entity or a directly referenced entity (exactly 1 FK hop). Transitive references (2+ hops) are async and must have been flagged in the Phase 0c gap scan.
|
|
215
|
-
5. Any set aggregate (`SUM`/`COUNT`/`AVG`/`MIN`/`MAX` over `[set].[field]`) is a Generated (`G`) column — never a Formula (`F`) column (an `F` set-aggregate silently renders empty) — and aggregates only a **physical** child column (`D` or same-row `G`), never a virtual `F`/`R`/`S` child.
|
|
216
|
-
6. Every seeded entity handles audit traits: if it uses `audit-full`, every seed record sets `created_by`/`last_updated_by` to the System user `0` (else use `audit` / don't seed it) — `audit-full`'s required user columns otherwise fail seed install.
|
|
217
|
-
|
|
218
|
-
Once all checks pass, present a brief summary (entity count, action count) and ask for final confirmation before calling `dforge_module_create`.
|
|
219
|
-
|
|
220
|
-
**Sub-steps:**
|
|
221
|
-
|
|
222
|
-
1. **Propose the entity inventory.** Re-read `_brief/00-intake.md`'s purpose and user-verbs sections. Derive an entity list: each meaningful "thing the user verbs against" tends to become an entity. Present as:
|
|
223
|
-
```
|
|
224
|
-
Proposed entities (N):
|
|
225
|
-
- <entity_code> — <one-line description, ties to a verb / use case from intake>
|
|
226
|
-
- ...
|
|
227
|
-
```
|
|
228
|
-
Apply the validate-and-reflect rule: "Here's what I think the module needs. Right shape and scope? Add / remove / merge?" Loop with the user until they explicitly approve the list.
|
|
229
|
-
|
|
230
|
-
Write the approved inventory to `_brief/01-domain.md`.
|
|
231
|
-
|
|
232
|
-
2. **Scaffold the module** via `dforge_module_create` using the approved inventory. Preview the file map, get approval, then user writes.
|
|
233
|
-
|
|
234
|
-
3. **Per-entity loop.** For each entity in order, propose fields + traits + references. **Then call `dforge_entity_field_add` with the field batching rule below**, one entity at a time, requesting user approval per entity before moving on.
|
|
235
|
-
|
|
236
|
-
4. **Extension entities last.** If extending another module's entity, use `extends: "module.entity"`, `toString: null` (inherits base), and a dotted manifest key. **Snapshot the base entity's current fields via `dforge_module_inspect` on the dependency dir** (when available) so you know what's inherited; if the dependency dir is not locally available, document the known base fields from `docs/DESIGN.md` and note in `_brief/changelog.md` that base-entity field drift is the user's responsibility to track.
|
|
237
|
-
|
|
238
|
-
**Before the first field of each entity** (and again whenever the element type changes), load the matching rows from the Loading policy table — fields, reference/set columns, formula columns, traits, number sequences. Do not call `dforge_entity_field_add` for a type without having read its reference + example first.
|
|
239
|
-
|
|
240
|
-
**Field batching rule** (the only Phase-1 exception to the hard rule):
|
|
241
|
-
|
|
242
|
-
A field is **batchable** only if ALL of these are true: scalar primitive (string / integer / decimal / boolean / date), no FK or Reference, no `formula`, and the nullability is unambiguous (e.g. required-not-null per intake context). Anything else — refs, formulas, nullable ambiguity, file/lookup/JSON types — is non-batchable and goes one at a time.
|
|
243
|
-
|
|
244
|
-
**Exit criteria:** every entity has at least PK + audit traits + 1 user-visible field; FK references resolve; manifest's `entities` map reflects reality.
|
|
245
|
-
|
|
246
|
-
## Phase 2 — Behavior (optional sub-steps)
|
|
247
|
-
|
|
248
|
-
**Preconditions:** Phase 1 complete.
|
|
249
|
-
|
|
250
|
-
Phase 2 covers four kinds of behavior — all optional, all individually skip-able. Each fires action logic, but the **trigger** differs:
|
|
251
|
-
|
|
252
|
-
| Sub-step | Fires when | File | Tool | Use when |
|
|
253
|
-
|---|---|---|---|---|
|
|
254
|
-
| 2a Actions | user clicks a button | `ui/actions.json` + `logic/actions/*.dsl` | `dforge_action_add` | bulk operations, business workflows, anything that needs user input via params |
|
|
255
|
-
| 2b Triggers | DB event happens (insert/update/delete/status_change) | `logic/triggers.json` | `dforge_trigger_add` | reactive automation: "when X happens, do Y" without user action |
|
|
256
|
-
| 2c Scheduled jobs | cron timer | `logic/jobs.json` | `dforge_job_add` | periodic work: nightly cleanup, daily summary, hourly poll |
|
|
257
|
-
| 2d Webhooks | DB event happens → POSTs to external URL | `logic/webhooks.json` | `dforge_webhook_add` | integrations: Slack notifications, Zapier, external dashboards, audit log shipping |
|
|
258
|
-
|
|
259
|
-
Skip a sub-step entirely if the user has no need for it. Do NOT fabricate behavior to fill a sub-step. Phase 2 can be completely skipped for pure CRUD modules.
|
|
260
|
-
|
|
261
|
-
### 2a. Actions — user-triggered
|
|
262
|
-
|
|
263
|
-
**Before authoring any action, load the full "action" row from the Loading policy table** — `dforge://reference/action-dsl`, `dforge://example/logic/actions/mark_done.dsl`, `dforge://example/ui/actions.json`, AND `dforge://docs/dsl`. All four are required: the wrong-field-access / wrong-batch-flag / wrong-or-missing `ui/actions.json`-property mistakes only surface when you cross-check them. (`dforge://docs/conventions` does not cover the DSL grammar.)
|
|
264
|
-
|
|
265
|
-
Call `dforge_action_add` per action — one at a time — with the full DSL body. Confirm with the user before each call.
|
|
266
|
-
|
|
267
|
-
### 2b. Triggers — DB-event-driven
|
|
268
|
-
|
|
269
|
-
Load the "trigger" row from the Loading policy table (`dforge://schema/triggers`). Trigger conditions use the same single-line `[field] op value` syntax as `canExecute:` — see `dforge://docs/dsl`.
|
|
270
|
-
|
|
271
|
-
For each trigger, propose: entity + event + (optional) condition formula + target action + async flag. Use `dforge_trigger_add`. Triggers reference EXISTING actions — make sure the target action was added in Phase 2a before creating any trigger that references it.
|
|
272
|
-
|
|
273
|
-
**Async vs sync:** `async: true` runs the action in the background after the triggering transaction commits — recommended for slow actions (emails, external API calls). `async: false` runs in the same transaction; action failure rolls back the original DB change.
|
|
274
|
-
|
|
275
|
-
### 2c. Scheduled jobs — cron-driven
|
|
276
|
-
|
|
277
|
-
Load the "scheduled job" row from the Loading policy table (`dforge://reference/jobs` + `dforge://schema/jobs`).
|
|
278
|
-
|
|
279
|
-
Constraints baked into the tool:
|
|
280
|
-
- Action MUST NOT use record-context (`[field]`) syntax — jobs run as system user with NO current record. Wrap any record-context action in a thin job-friendly action that uses `select()` (or `query()`) to fetch the records it needs.
|
|
281
|
-
- `timeout` is required, ≤ 3600s.
|
|
282
|
-
- If `timeout > 300`, you MUST set `jobClass: 'long_running'`.
|
|
283
|
-
|
|
284
|
-
Use `dforge_job_add` per job.
|
|
285
|
-
|
|
286
|
-
### 2d. Webhooks — outbound HTTP
|
|
287
|
-
|
|
288
|
-
Load the "webhook" row from the Loading policy table (`dforge://schema/webhooks`).
|
|
289
|
-
|
|
290
|
-
For each webhook: entity + event + endpoint URL + (optional) condition + (optional) payload shape (include/exclude/includeOld). Use `dforge_webhook_add`.
|
|
291
|
-
|
|
292
|
-
For authenticated endpoints: put bearer tokens / API keys behind `getSecret()` (configure secret in module's secrets), reference in headers as `"Authorization": "$secret:<secret_cd>"` — the platform resolves at fire time.
|
|
293
|
-
|
|
294
|
-
**Exit criteria for Phase 2:** every action / trigger / job / webhook you added is intended (user-requested, not fabricated to fill space) and references existing entities + actions. Compilation is validated at install in Phase 6.
|
|
295
|
-
|
|
296
|
-
## Phase 3 — Views (required) + Reports (optional)
|
|
297
|
-
|
|
298
|
-
**Preconditions:** Phase 1 complete.
|
|
299
|
-
|
|
300
|
-
**Before the first element of each type**, load the matching rows from the Loading policy table — data views, menus, filters, reports. Do not call `dforge_view_add`, `dforge_view_modify`, or `dforge_report_add` without having read its reference + example first.
|
|
301
|
-
|
|
302
|
-
### 3a. Default grids (required, do FIRST)
|
|
303
|
-
|
|
304
|
-
For every entity in the manifest, call `dforge_view_add` with `viewType: "grid"` and `dataSources: [{ entityCode: <entity>, columns: [...] }]`.
|
|
305
|
-
|
|
306
|
-
**View naming.** View codes in `ui/data_views.json` are semantic — convention is the entity name (`feedback_item`), the plural (`invoices`), or descriptive (`invoices_kanban`, `feedback_by_status`). Do NOT use the literal code `default`. When `ui/folders.json` entities reference `viewName: "default"`, the platform resolves that to the entity's first view declared in `data_views.json` — it's a fallback alias, not a required view code. (The scaffolder already wrote a default grid keyed by entity code in Phase 1, so often you'll `view_modify` it rather than `view_add`.)
|
|
307
|
-
|
|
308
|
-
**Do not propose any specialized view until every entity has its default grid.**
|
|
309
|
-
|
|
310
|
-
### 3b. Specialized views (optional, only after 3a complete)
|
|
311
|
-
|
|
312
|
-
Propose a specialized view (kanban / calendar / list-with-levels / tree-grid / master-detail) **only when one of these objective triggers fires**:
|
|
313
|
-
|
|
314
|
-
- The user explicitly mentioned the visualization ("show leads as a kanban", "we need a calendar for tasks").
|
|
315
|
-
- The entity has a status / stage / kind field with **3 or more discrete values** in a dropdown — kanban candidate.
|
|
316
|
-
- The entity has a required date/time field intended for scheduling — calendar candidate.
|
|
317
|
-
- The entity self-references (parent FK to itself) — tree-grid candidate.
|
|
318
|
-
- The entity has a 1:N detail child with `parentSetField` declared — list-with-levels or master-detail candidate.
|
|
319
|
-
|
|
320
|
-
If none of these fire, skip specialized views for that entity. Load the specialized-view row from the Loading policy table (`dforge://schema/data-views` for the `viewConfig` shape) before calling `dforge_view_add`.
|
|
321
|
-
|
|
322
|
-
### 3c. Reports (optional)
|
|
323
|
-
|
|
324
|
-
Add reports only when management aggregation/grouping isn't covered by views. `dforge_report_add` with layout + datasets (Query type with entityCd + filter + sort, or Stored Procedure). Load the report row from the Loading policy table (`dforge://schema/reports`) first.
|
|
325
|
-
|
|
326
|
-
**Exit criteria:** every entity has a default grid; every specialized view has a stated trigger; reports cover the stated reporting use cases.
|
|
327
|
-
|
|
328
|
-
## Phase 4 — Polish: settings, translations, seed (mostly optional)
|
|
329
|
-
|
|
330
|
-
**Preconditions:** Phase 3 complete.
|
|
331
|
-
|
|
332
|
-
**Before the first element of each type**, load the matching rows from the Loading policy table — settings, translations, number sequences (seed data), print templates, saved queries.
|
|
333
|
-
|
|
334
|
-
- **Settings**: `dforge_setting_add` per configurable value the user requested.
|
|
335
|
-
- **Translations** (required if intake declared non-English locales): files under `translations/<locale>.json`. **If the user defers translation authoring, append to `_brief/changelog.md`: "Translation files for [locales] are incomplete. Phase 6 install will fail translation completeness validation until added." Remind the user before calling `dforge_module_pack`.**
|
|
336
|
-
- **Seed data**: only when the module needs reference data on install.
|
|
337
|
-
|
|
338
|
-
**Exit criteria:** any configurable value the user requested is exposed as a setting; if non-English locales were declared, translations exist OR the deferral warning is logged.
|
|
339
|
-
|
|
340
|
-
## Phase 5 — Security
|
|
341
|
-
|
|
342
|
-
**Preconditions:** Phases 1 and 3a complete (you need entity codes and default grid views to grant rights on; actions and reports added in Phases 2 and 3b/3c can be granted as they are added).
|
|
343
|
-
|
|
344
|
-
### 5a. Roles + rights matrix (required)
|
|
345
|
-
|
|
346
|
-
**Before any role or folder work**, load the "security roles or folders" row from the Loading policy table (`dforge://reference/security` + `dforge://example/security/roles.json`; `dforge://reference/filters` for folder row filters). Do not call `dforge_role_add`, `dforge_role_right_set`, or `dforge_folder_add` without having read it.
|
|
347
|
-
|
|
348
|
-
1. **Inspect first.** Run `dforge_module_inspect` and read the `roles` array. The scaffolder pre-creates `<code>.admin` with `SIUDC` on every entity declared at scaffold time. That role exists already — don't try to re-create it.
|
|
349
|
-
2. **Derive role inventory FROM the intake's user types and verbs — never default to a fixed taxonomy.** Re-read `_brief/00-intake.md`'s `User types` section. For each distinct user type, propose ONE role named `<code>.<user-type>` (e.g. intake said "any signed-in user submits + admins triage" → `<code>.user` (covers the "submits" verb) + the existing scaffolded `<code>.admin` (covers triage). If intake mentioned "approvers" or "auditors" or "managers" or any other group, derive roles for those too.) **Forbidden:** spinning up a generic `admin/contributor/viewer` matrix when the user didn't ask for it. The rights set should map to the verbs each user type does, not to a textbook role hierarchy.
|
|
350
|
-
3. Reflect the proposed role list back to the user before computing rights: "Based on intake, I see these user types → these roles: `<list>`. Right?" Get explicit confirmation. If the user clarifies / adds / removes, re-list and re-confirm.
|
|
351
|
-
4. Show the rights matrix as a table (rows = entities/actions/reports, columns = the confirmed roles, cells = rights string). Each cell explained by the verb-to-right mapping you derived. Get user sign-off on the matrix.
|
|
352
|
-
5. **For new roles**: call `dforge_role_add`. **For amending existing roles** (the scaffolded admin, or grants on actions/reports added in Phases 2-3 that aren't yet in any role): call `dforge_role_right_set` per grant — it's the smallest tool and doesn't conflict with the scaffolded admin role. Calling `dforge_role_add` against an existing role code fails — use `role_right_set` to amend instead.
|
|
353
|
-
|
|
354
|
-
**Rights semantics** (additive — multiple roles UNION, never revoke):
|
|
355
|
-
- Entities: any subset of `SIUDC` (Select / Insert / Update / Delete / Clone)
|
|
356
|
-
- Actions / reports: `E` (Execute), or omit to deny
|
|
357
|
-
- **Object key format:** same-module entity bare (`product`); cross-module entity dotted (`fin.invoice`); action/report/folder use a **colon** prefix (`action:approve`, `report:summary`, `folder:east`). A dot before an action/report/folder code is rejected as an unknown object.
|
|
358
|
-
|
|
359
|
-
### 5b. Security folders (optional)
|
|
360
|
-
|
|
361
|
-
Only if intake said data must be partitioned per folder (multi-warehouse, multi-region, multi-tenant-like). Default: root only.
|
|
362
|
-
|
|
363
|
-
If needed: `dforge_folder_add` per sub-folder, passing `entities` with `rowFilter` (SQL string OR canonical `{c,o,v}` / `{g,i:[]}` filter).
|
|
364
|
-
|
|
365
|
-
**Exit criteria:** run `dforge_module_inspect` and verify every entity code in the manifest appears in at least one role's rights map with at least `S` (Select); list any uncovered entity as a gap before advancing to Phase 6. If folders were declared, every folder has security mapped.
|
|
366
|
-
|
|
367
|
-
> ⛔ **GATE — `dforge_module_pack` enforces entity coverage.** Pack refuses to build the tarball if any entity has no role granting `S`, and it warns about actions/reports with no `E` grant. The platform installs a security-less module without complaint (it just becomes inaccessible), so this is the only place it's caught. **Don't lean on the gate** — derive real persona roles from the intake user types here (not just the scaffolded `<code>.admin`), and grant `E` on the actions/reports each persona uses.
|
|
368
|
-
|
|
369
|
-
## Phase 6 — Verify (required, non-skippable)
|
|
370
|
-
|
|
371
|
-
**Preconditions:** all required phases complete: 0a, 0b, 0c, 0d, 1, 3a, 5a. Optional phases (2, 3b/3c, 4, 5b) are not preconditions — explicitly skipped optional phases do not block Phase 6.
|
|
372
|
-
|
|
373
|
-
**Steps:**
|
|
374
|
-
|
|
375
|
-
### Step 1 — Automated validation (blocking gate)
|
|
376
|
-
|
|
377
|
-
Call **`dforge_module_validate`** on the module dir first. It runs the cross-reference checks offline (dangling FK/reference targets, a missing hidden-FK column, view columns / menu dataViewCodes / role rights pointing at things that don't exist, entities with no Select grant) — the errors that otherwise only surface at install. Read `files["_validate.json"]`: **every `error` must be fixed** (apply the backtrack protocol) before continuing; review `warning`s with the user. Re-run until `ok: true`. This is faster and more reliable than eyeballing — but it is structural only; it does not judge intent, so still do Step 2.
|
|
378
|
-
|
|
379
|
-
### Step 2 — Pre-pack self-review (blocking gate)
|
|
380
|
-
|
|
381
|
-
Load `dforge://reference/validation-checklist`. Run through **every section** in order. Surface each failure to the user and apply the backtrack protocol before proceeding. Do not advance to packing until all checks pass.
|
|
382
|
-
|
|
383
|
-
**Top install-blockers — scan these first** (each is a documented real install failure the platform validator rejects):
|
|
384
|
-
|
|
385
|
-
1. **DSL dates:** `execute:` blocks use lowercase `now()`; never `TODAY()`/`NOW()` (formula-only → `'TODAY' is not defined`).
|
|
386
|
-
2. **Roll-ups:** a total over a child set is a Generated (`G`) column with `SUM([set].[field])` (`dbDatatype` + `formula`) — never a Formula (`F`) column (an `F` set-aggregate silently renders empty). Aggregate only a **physical** child column (`D` or same-row `G`); a virtual `F`/`R`/`S` child fails install (→ `db_error: column old.<field> does not exist`).
|
|
387
|
-
3. **Rights keys:** actions/reports/folders use a **colon** (`action:x`, `report:x`, `folder:x`); entities are bare or cross-module-dotted; deny by omitting the key, never `""`.
|
|
388
|
-
4. **Manifest:** no `translations` key (translation files auto-discovered; non-English locales → `supportedLocales`).
|
|
389
|
-
5. **Column defaults:** set via `formula` / `numberSequence` / DSL — never a `defaultValue` key on an entity field.
|
|
390
|
-
6. **Seed + traits:** seeded `audit-full` entities set `created_by`/`last_updated_by: 0` (System user) in every record — otherwise use `audit` or don't seed (`required column 'created_by' … is not present in seed records`).
|
|
391
|
-
|
|
392
|
-
Key areas (full checklist):
|
|
393
|
-
|
|
394
|
-
- **Manifest**: `moduleId` is a valid UUID; `version` and `dbSchemaVersion` are set; `supportedLocales` lists every non-English locale that has a `translations/<locale>.json` file (and `en`/`en-US` is NOT listed); `security` block has both `roles` and `folders`.
|
|
395
|
-
- **Entities**: every entity has `identity` + `audit` traits, a `toString` template, and the FK+Reference pattern applied wherever a relation exists (hidden FK column `flags: "EM"` + visible Reference column `columnType: "R"` + entry in `references` block).
|
|
396
|
-
- **Formula columns** (`columnType: "F"`): have `baseDatatypeCd`, no `dbDatatype`, `flags: "V"`.
|
|
397
|
-
- **Flags**: only `V`, `I`, `E`, `M`, `H` used — no `U`, `S`, or `P`.
|
|
398
|
-
- **Data views**: every entity has a default grid; `dataSources` array present at root; sort uses `"order": ["-col", "col"]` string-array (never `"sort": [{column_cd, direction}]`).
|
|
399
|
-
- **Menus**: leaf items have `dataViewCode` (not `viewCode`); icons are Bootstrap names without the `bi-` prefix; section nodes omit `itemType`.
|
|
400
|
-
- **Security**: every entity code in the manifest appears in at least one role's rights map; `rights` key used (not `entityRights`); entity rights use `SIUDC` letters; actions/reports use `E`.
|
|
401
|
-
- **Actions**: every `script` value in `ui/actions.json` is a bare filename (no path, no `.dsl` extension); every action referenced by a trigger or job exists in `ui/actions.json`.
|
|
402
|
-
- **Seed data**: numeric PKs; parent entities loaded before children; no circular references.
|
|
403
|
-
- **Translations**: a `translations/<locale>.json` file exists for every locale in `supportedLocales` **plus the `en-US` base**; every trait-provided field (`created_at`, `updated_at`, etc.) has a translation entry in each file; a `roles` section carries a `label` for **every** role in `security/roles.json` (completeness-enforced in every locale incl. en-US — keys are module-qualified role codes like `crm.admin`).
|
|
404
|
-
|
|
405
|
-
### Step 3 — Translation deferral check
|
|
406
|
-
|
|
407
|
-
Read `_brief/changelog.md`. If a translation deferral warning is present ("Translation files for [locales] are incomplete"), halt here. Tell the user: "Translation files must be completed before packing — install will fail translation completeness validation." Do not proceed to Step 4 until resolved.
|
|
408
|
-
|
|
409
|
-
### Step 4 — Final inspect + version audit
|
|
410
|
-
|
|
411
|
-
Run `dforge_module_inspect`. Show a one-line summary: entity count, view count, action count, role count. Then confirm version strings with the user:
|
|
412
|
-
|
|
413
|
-
- **`version`**: always bump (semver) before packing.
|
|
414
|
-
- **`dbSchemaVersion`**: bump only if any entity fields were added, removed, or type-changed since the last install. If unsure, compare current entity schemas against the last committed state.
|
|
415
|
-
|
|
416
|
-
Get user confirmation on both version strings before packing.
|
|
417
|
-
|
|
418
|
-
### Step 5 — Pack + install
|
|
419
|
-
|
|
420
|
-
1. `dforge_module_pack` → produces `.dforge` tarball. (Blocked if any entity lacks a role granting Select — the Phase 5a gate; fix security coverage and re-run.)
|
|
421
|
-
2. `dforge_module_install` with `DFORGE_URL` / `DFORGE_TOKEN`. Runs the full server-side validator.
|
|
422
|
-
|
|
423
|
-
**Install-fix loop (mandatory):**
|
|
424
|
-
|
|
425
|
-
1. Call `dforge_module_install` yourself. Do not ask the user to run the install command for you.
|
|
426
|
-
2. If the result has `ok: false`, read the returned `output` in full. It is the raw CLI/server validator output and is the input for the next fix.
|
|
427
|
-
3. If the failure is a module defect, fix it yourself. Use this table to identify which phase to backtrack to, then apply the backtrack protocol with the smallest suitable tool or file edit.
|
|
428
|
-
4. Re-run Step 1 (`dforge_module_validate`), Step 2 (self-review for the touched area), Step 5 pack, and Step 5 install.
|
|
429
|
-
5. Repeat this loop until install succeeds or the remaining failure is clearly an environment/tooling issue from the Tool failure protocol.
|
|
430
|
-
|
|
431
|
-
Common module-defect patterns:
|
|
432
|
-
|
|
433
|
-
| Install error pattern | Backtrack to |
|
|
13
|
+
| `dforge-module-design` | 0a–0d | Identity, intake, design doc, pre-scaffold validation. Ends at `readyToScaffold: true`. |
|
|
14
|
+
| `dforge-module-build` | 1–5 | Entities, behavior, views/menus, polish, security. |
|
|
15
|
+
| `dforge-module-ship` | 6 | Validate → pre-pack review → pack → install-fix loop. |
|
|
16
|
+
|
|
17
|
+
**Why split:** each stage needs a different half of the knowledge base. Phase 0
|
|
18
|
+
needs none of the field-type/DSL detail; Phase 6 needs none of the intake
|
|
19
|
+
guardrails. Loading only the active stage is what keeps a full module build
|
|
20
|
+
inside one context window.
|
|
21
|
+
|
|
22
|
+
## Do this first, every session
|
|
23
|
+
|
|
24
|
+
1. **Get `moduleDir`.** If the user hasn't given one: _"Where should the module
|
|
25
|
+
directory live? (absolute path)"_ — ask before any tool call.
|
|
26
|
+
2. **Call `dforge_module_plan({ action: "check", moduleDir })`.** It reads the
|
|
27
|
+
on-disk state and returns `currentPhase`, `nextSkill`, and `nextStep`.
|
|
28
|
+
3. **Invoke the skill it names** (`nextSkill`) and follow it. If the module
|
|
29
|
+
already exists, also run `dforge_module_inspect` first so you know what's
|
|
30
|
+
there before proposing anything.
|
|
31
|
+
|
|
32
|
+
That single call is the whole routing decision — do not guess the phase from
|
|
33
|
+
file listings, and do not load a stage skill you weren't routed to.
|
|
34
|
+
|
|
35
|
+
**If the user asks to skip Phase 0** ("just scaffold it", "skip the docs"):
|
|
36
|
+
respond — _"Phase 0 documents are required before scaffolding. They take 15–30
|
|
37
|
+
minutes and prevent hours of backtracking. Let me check where we are."_ — then
|
|
38
|
+
call `check`. `dforge_module_create` is gated at the tool level and will refuse
|
|
39
|
+
regardless.
|
|
40
|
+
|
|
41
|
+
## Phase ledger
|
|
42
|
+
|
|
43
|
+
`dforge_module_plan` tracks Phase 0 by which artifact files exist, and Phases
|
|
44
|
+
1–6 by a ledger in `docs/phase.json` **plus** evidence read from the module
|
|
45
|
+
itself (entities without fields, entities without a view, entities without a
|
|
46
|
+
Select grant). So:
|
|
47
|
+
|
|
48
|
+
- **Record each phase as you finish it** —
|
|
49
|
+
`dforge_module_plan({ action: "complete_phase", moduleDir, phase: "3", note: "..." })`.
|
|
50
|
+
For an optional phase the user declined, add `skipped: true`.
|
|
51
|
+
- A resumed session then knows the difference between "Phase 2 skipped
|
|
52
|
+
deliberately" and "Phase 2 not started". Without the record, it can't.
|
|
53
|
+
- `check` reports `gaps` when the ledger and the module disagree. Trust the
|
|
54
|
+
gaps: they're derived from the files.
|
|
55
|
+
|
|
56
|
+
## Tool inventory
|
|
57
|
+
|
|
58
|
+
Full descriptions are on the tools themselves — this is the map.
|
|
59
|
+
|
|
60
|
+
| Group | Tools |
|
|
434
61
|
|---|---|
|
|
435
|
-
|
|
|
436
|
-
|
|
|
437
|
-
|
|
|
438
|
-
|
|
|
439
|
-
|
|
|
440
|
-
|
|
|
441
|
-
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
62
|
+
| Lifecycle | `dforge_module_plan`, `dforge_module_create`, `dforge_module_inspect`, `dforge_module_validate`, `dforge_module_pack`, `dforge_module_install` |
|
|
63
|
+
| Import | `dforge_module_import`, `dforge_dbml_import` |
|
|
64
|
+
| Entities | `dforge_entity_add`, `dforge_entity_rename`, `dforge_entity_delete` |
|
|
65
|
+
| Fields | `dforge_entity_field_add`, `_modify`, `_rename`, `_remove` |
|
|
66
|
+
| **Composite (prefer these)** | `dforge_entity_reference_add` (relation), `dforge_entity_rollup_add` (child total), `dforge_entity_status_add` (status column) |
|
|
67
|
+
| Behavior | `dforge_action_add`, `dforge_action_check`, `dforge_trigger_add`, `dforge_job_add`, `dforge_webhook_add` |
|
|
68
|
+
| UI | `dforge_view_add`, `dforge_view_modify`, `dforge_menu_add`, `dforge_report_add` |
|
|
69
|
+
| Polish | `dforge_setting_add`, `dforge_translation_sync`, `dforge_seed_add` |
|
|
70
|
+
| Security | `dforge_role_add`, `dforge_role_right_set`, `dforge_folder_add` |
|
|
71
|
+
| Cross-cutting | `dforge_dependency_add` |
|
|
72
|
+
|
|
73
|
+
**Composite tools first.** A relation, a roll-up, and a status column each span
|
|
74
|
+
several coordinated keys, and hand-assembling them from `entity_field_add` is
|
|
75
|
+
the top source of broken modules. The composite tools emit the whole shape and
|
|
76
|
+
can't express the broken variants. Reach for `entity_field_add` only for a
|
|
77
|
+
plain scalar column.
|
|
78
|
+
|
|
79
|
+
## Writing files: preview vs `apply`
|
|
80
|
+
|
|
81
|
+
Every patch tool returns `{ summary, files, deletes? }` for the client to write
|
|
82
|
+
— so you can preview a diff with the user first. Passing **`apply: true`**
|
|
83
|
+
instead writes the files (and honours `deletes`) directly and returns only the
|
|
84
|
+
paths touched.
|
|
85
|
+
|
|
86
|
+
- **Preview (default)** when the user is reviewing the change, and for anything
|
|
87
|
+
in Phase 0.
|
|
88
|
+
- **`apply: true`** for routine patches inside an already-approved plan, and for
|
|
89
|
+
every `rename`/`delete` refactor — it guarantees the `deletes` are applied,
|
|
90
|
+
which is easy to drop by hand.
|
|
91
|
+
- Either way, **`_inspect.json` / `_validate.json` / `_action_check.json` are
|
|
92
|
+
report payloads, never files** — don't write them to disk.
|
|
93
|
+
|
|
94
|
+
## Hard rules — all stages
|
|
95
|
+
|
|
96
|
+
1. **Co-pilot stance.** Draft → propose → user approves → tool call. Never write
|
|
97
|
+
without confirmation. Read-only tools (`inspect`, `validate`, `action_check`)
|
|
98
|
+
need no gate.
|
|
99
|
+
2. **Inspect before patching.** `dforge_module_inspect` at session start and
|
|
100
|
+
after every backtrack. Parse `files["_inspect.json"]`, not just the summary.
|
|
101
|
+
3. **One thing at a time.** One question per turn; one entity / view / role /
|
|
102
|
+
action / report proposed per turn. The only exception is the Phase 1 field
|
|
103
|
+
batching rule (see `dforge-module-build`).
|
|
104
|
+
4. **Validate and reflect.** After every user answer, restate what you understood
|
|
105
|
+
and ask "Right?" before moving on. Zero ambiguity going into the next step.
|
|
106
|
+
5. **Load before authoring.** Pull the matching `dforge://reference/<name>` +
|
|
107
|
+
`dforge://example/<path>` resource before authoring any element type,
|
|
108
|
+
including inside a backtrack. They're **MCP resources — load by URI, not by
|
|
109
|
+
filesystem path**; your CWD is the module dir, so `references/*.md` won't
|
|
110
|
+
resolve on disk. If a needed resource fails to load, stop and tell the user —
|
|
111
|
+
never invent a schema from memory.
|
|
112
|
+
6. **Don't invent domain content.** Fields, codes, roles and relationships come
|
|
113
|
+
from the user's domain. Never default to a generic `admin/contributor/viewer`
|
|
114
|
+
taxonomy they didn't ask for.
|
|
115
|
+
7. **A step is done only when its file is written AND reviewed.** Deciding what
|
|
116
|
+
you *would* write is not "done". Don't advance a phase on intent.
|
|
117
|
+
8. **Tabs in JSON, trailing newline** — the tools already emit this; don't
|
|
118
|
+
reformat.
|
|
119
|
+
|
|
120
|
+
## Tool failure protocol — all stages
|
|
121
|
+
|
|
122
|
+
1. **Surface the raw error verbatim.** Don't paraphrase.
|
|
123
|
+
2. **Classify before asking for help.** If the output names a module defect, fix
|
|
124
|
+
it yourself with the smallest suitable tool, then re-run validation. The error
|
|
125
|
+
is already in the tool result — never ask the user to paste it back.
|
|
126
|
+
3. **Ask the user only for environment issues** you can't fix from module files:
|
|
127
|
+
missing CLI, missing/expired credentials, unreachable tenant, permissions, or
|
|
128
|
+
a path outside the workspace.
|
|
129
|
+
4. **Do not advance the phase** until the failing tool succeeds.
|
|
463
130
|
|
|
464
|
-
|
|
465
|
-
- **Remove a field** → `dforge_entity_field_remove` (cascade-cleans views/seed/paired Reference; warns formula/cross-entity).
|
|
466
|
-
- **Rename an entity** → `dforge_entity_rename` (PK `{old}_id → {new}_id`, link.entity, references, view entityCode, role keys, action entity, folder bindings, seed).
|
|
467
|
-
- **Delete an entity** → `dforge_entity_delete` (file + seed + manifest + roles + folders + view sources).
|
|
131
|
+
Known causes worth distinguishing:
|
|
468
132
|
|
|
469
|
-
|
|
133
|
+
- **`pack`/`install` "command not found"** → dforge-cli isn't on PATH. Tell the
|
|
134
|
+
user: `npm install -g @dforge-core/dforge-cli`, then re-run.
|
|
135
|
+
- **`install` HTTP 401/403 or connection refused** → credentials/connectivity,
|
|
136
|
+
NOT a module defect. Verify `DFORGE_URL` / `DFORGE_TOKEN`. Do not backtrack.
|
|
137
|
+
- **`install` `ok: false` with validation/compile/schema output** → a module
|
|
138
|
+
defect. Treat `output` as the source of truth and run the fix loop in
|
|
139
|
+
`dforge-module-ship`.
|
|
470
140
|
|
|
471
|
-
|
|
141
|
+
## Resource index
|
|
472
142
|
|
|
473
|
-
|
|
143
|
+
Load these by URI, one per step, at the step that needs them.
|
|
474
144
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
-
|
|
478
|
-
|
|
479
|
-
-
|
|
480
|
-
|
|
145
|
+
**Per-element references** — `dforge://reference/<name>`:
|
|
146
|
+
`field-types`, `flags`, `column-types`, `column-domains`, `formulas`, `traits`,
|
|
147
|
+
`data-views`, `menus`, `action-dsl`, `filters`, `security`, `reports`,
|
|
148
|
+
`settings`, `jobs`, `number-sequences`, `document-extraction`,
|
|
149
|
+
`print-templates`, `translations`, `queries`, `schema-import`, `excel-import`,
|
|
150
|
+
`data-migration`, `manifest`, `validation-checklist`, `conventions`.
|
|
481
151
|
|
|
482
|
-
|
|
152
|
+
**Schemas** (fallback, when a reference points at one) — `dforge://schema/<name>`:
|
|
153
|
+
`manifest`, `entity`, `domains`, `data-views`, `folders`, `menus`, `roles`,
|
|
154
|
+
`jobs`, `seed-data`, `traits`, `webhooks`, `triggers`, `print-templates`,
|
|
155
|
+
`settings`, `reports`.
|
|
483
156
|
|
|
484
|
-
|
|
157
|
+
**Docs**: `dforge://docs/dsl` (full DSL grammar + built-ins),
|
|
158
|
+
`dforge://docs/conventions` (cross-module extension work — §1b only).
|
|
485
159
|
|
|
486
|
-
**
|
|
160
|
+
**Examples** — `dforge://example/<path>` (simple-todo) and
|
|
161
|
+
`dforge://example/matrix-budget/<path>`. These are mandatory structure
|
|
162
|
+
validators: copy their shapes, don't invent.
|
|
487
163
|
|
|
488
|
-
|
|
164
|
+
**Script**: `dforge://script/xlsx-to-model` (stdlib Python .xlsx extractor).
|