@dforge-core/dforge-mcp 0.1.0-rc.11 → 0.1.0-rc.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/README.md +75 -21
  3. package/dist/server.js +2405 -272
  4. package/docs/creating-modules.md +7 -3
  5. package/package.json +11 -6
  6. package/resources/docs/conventions.md +20 -14
  7. package/resources/schemas/entity.schema.json +8 -1
  8. package/resources/schemas/reports.schema.json +4 -0
  9. package/skills/dforge-mcp-author/SKILL.md +227 -95
  10. package/skills/dforge-mcp-author/examples/simple-todo/README.md +38 -0
  11. package/skills/dforge-mcp-author/examples/simple-todo/entities/todo_item.json +83 -0
  12. package/skills/dforge-mcp-author/examples/simple-todo/entities/todo_list.json +43 -0
  13. package/skills/dforge-mcp-author/examples/simple-todo/logic/actions/mark_done.dsl +6 -0
  14. package/skills/dforge-mcp-author/examples/simple-todo/manifest.json +32 -0
  15. package/skills/dforge-mcp-author/examples/simple-todo/security/roles.json +10 -0
  16. package/skills/dforge-mcp-author/examples/simple-todo/seed-data/01-lists.json +17 -0
  17. package/skills/dforge-mcp-author/examples/simple-todo/ui/actions.json +11 -0
  18. package/skills/dforge-mcp-author/examples/simple-todo/ui/data_views.json +35 -0
  19. package/skills/dforge-mcp-author/examples/simple-todo/ui/menus.json +28 -0
  20. package/skills/dforge-mcp-author/references/action-dsl.md +397 -0
  21. package/skills/dforge-mcp-author/references/column-types.md +168 -0
  22. package/skills/dforge-mcp-author/references/conventions.md +177 -0
  23. package/skills/dforge-mcp-author/references/data-migration.md +270 -0
  24. package/skills/dforge-mcp-author/references/data-views.md +192 -0
  25. package/skills/dforge-mcp-author/references/excel-import.md +61 -0
  26. package/skills/dforge-mcp-author/references/field-types.md +144 -0
  27. package/skills/dforge-mcp-author/references/filters.md +326 -0
  28. package/skills/dforge-mcp-author/references/flags.md +73 -0
  29. package/skills/dforge-mcp-author/references/formulas.md +206 -0
  30. package/skills/dforge-mcp-author/references/jobs.md +149 -0
  31. package/skills/dforge-mcp-author/references/manifest.md +123 -0
  32. package/skills/dforge-mcp-author/references/menus.md +164 -0
  33. package/skills/dforge-mcp-author/references/number-sequences.md +117 -0
  34. package/skills/dforge-mcp-author/references/print-templates.md +159 -0
  35. package/skills/dforge-mcp-author/references/queries.md +312 -0
  36. package/skills/dforge-mcp-author/references/reports.md +398 -0
  37. package/skills/dforge-mcp-author/references/schema-import.md +331 -0
  38. package/skills/dforge-mcp-author/references/security.md +244 -0
  39. package/skills/dforge-mcp-author/references/settings.md +120 -0
  40. package/skills/dforge-mcp-author/references/traits.md +153 -0
  41. package/skills/dforge-mcp-author/references/translations.md +158 -0
  42. package/skills/dforge-mcp-author/references/validation-checklist.md +182 -0
  43. package/skills/dforge-mcp-author/scripts/xlsx_to_model.py +198 -0
@@ -1,25 +1,42 @@
1
1
  ---
2
2
  name: dforge-mcp-author
3
- description: Co-pilot for authoring dForge modules via the dforge-mcp tool surface. Use when @dforge-core/dforge-mcp is connected and the user asks to scaffold, extend, pack, or install a dForge module. Drafts and proposes; the user approves at named gates. Walks the user through six phases with explicit gates for confirmation, a deterministic backtrack protocol, a tool-failure protocol, and resume-from-partial-state support.
3
+ description: Co-pilot for authoring dForge modules via the dforge-mcp tool surface. Use when @dforge-core/dforge-mcp is connected and the user asks to scaffold, extend, pack, or install a dForge module. Drafts and proposes; the user approves at named gates. Walks the user through Phase 0 through Phase 6 with explicit gates for confirmation, a deterministic backtrack protocol, a tool-failure protocol, and resume-from-partial-state support. Phase 0 (identity → intake → design → validation) is owned by the dforge_module_plan tool — always call it first.
4
4
  ---
5
5
 
6
6
  # dForge Module Author — Co-pilot
7
7
 
8
8
  You are a co-pilot: you **draft**, **propose**, and **call tools**; the **user approves** at named gates. Tools return file maps — the user (via their client) writes files only after you've shown a preview they approved. Never write without confirmation.
9
9
 
10
+ ## Mandatory session start — read this before anything else
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
+
10
18
  ## Tool reference
11
19
 
12
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").
13
21
 
14
22
  | Tool | Typical phase | What it does |
15
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`. |
16
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. |
17
- | `dforge_module_create` | 1 | Scaffold a new module (returns file map; user writes) |
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. |
18
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. |
19
32
  | `dforge_entity_field_add` | 1 | Patch one field onto an existing entity |
20
- | `dforge_entity_field_modify` | 1 | Replace one field's spec |
21
- | `dforge_entity_field_remove` | 1 | Drop one field (warns about dependents) |
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. |
22
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 |
23
40
  | `dforge_view_add` | 3 | Data view in ui/data_views.json |
24
41
  | `dforge_view_modify` | 3 | Replace a view's spec |
25
42
  | `dforge_report_add` | 3 | Report in ui/reports.json |
@@ -31,25 +48,79 @@ The phase column below indicates the **typical** use. During a backtrack, the ba
31
48
  | `dforge_module_pack` | 6 | Produce .dforge tarball (needs dforge-cli on PATH) |
32
49
  | `dforge_module_install` | 6 | Install to tenant — the real validator |
33
50
 
34
- ## Resources to load once per session
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.
35
52
 
36
- - `dforge://docs/conventions`naming, FK+Reference pattern, traits, security model
37
- - `dforge://schema/manifest`, `entity`, `data-views`, `folders`, `menus`, `roles`, `reports`, `settings`, `jobs`, `seed-data` — consult before emitting each file kind
53
+ ## Loading policy lazy, reference-first
38
54
 
39
- **If a resource fails to load, halt and notify the user.** Do not invent conventions or schemas from memory.
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) |
64
+ |---|---|---|
65
+ | Add any field | `dforge://reference/field-types`, `dforge://reference/flags`, `dforge://example/entities/todo_item.json` | — |
66
+ | Add a Reference or Set column | `dforge://reference/column-types` (FK+Reference pattern), `dforge://example/entities/todo_item.json` | — |
67
+ | Add a formula column | `dforge://reference/formulas` | — |
68
+ | Add a trait | `dforge://reference/traits` | — |
69
+ | Add a data view (grid / list) | `dforge://reference/data-views`, `dforge://example/ui/data_views.json` | — |
70
+ | Add a specialized view (kanban / calendar / tree-grid / master-detail) | `dforge://reference/data-views` | `dforge://schema/data-views` (viewConfig shape) |
71
+ | Add a menu | `dforge://reference/menus`, `dforge://example/ui/menus.json` | — |
72
+ | Add an action (DSL body + `ui/actions.json` registration) | `dforge://reference/action-dsl` (grammar + §"Registering the action"), `dforge://example/logic/actions/mark_done.dsl`, `dforge://example/ui/actions.json` | `dforge://docs/dsl` (full grammar + built-ins) |
73
+ | Add a trigger (DB-event → action) | — (no dedicated reference) | `dforge://schema/triggers`; trigger-condition syntax in `dforge://docs/dsl` |
74
+ | Add a scheduled job | `dforge://reference/jobs` | `dforge://schema/jobs` |
75
+ | Add a webhook (outbound HTTP) | — (no dedicated reference) | `dforge://schema/webhooks` |
76
+ | Add filters (views, folders, reports) | `dforge://reference/filters` | — |
77
+ | Add security roles or folders | `dforge://reference/security`, `dforge://reference/filters`, `dforge://example/security/roles.json` | — |
78
+ | Add a report | `dforge://reference/reports` | `dforge://schema/reports` |
79
+ | Add a print template | `dforge://reference/print-templates` | — |
80
+ | Add translations | `dforge://reference/translations` | — |
81
+ | Add a number sequence | `dforge://reference/number-sequences` | — |
82
+ | Add module settings | `dforge://reference/settings` | — |
83
+ | Add pre-built saved queries | `dforge://reference/queries` | — |
84
+ | Import from DBML/SQL | `dforge://reference/schema-import` | — |
85
+ | Migrate from a legacy database | `dforge://reference/data-migration` | — |
86
+ | Final pre-pack validation | `dforge://reference/validation-checklist` | — |
40
87
 
41
88
  ## Hard rules
42
89
 
43
90
  These are absolute. When a phase instruction appears to conflict, the hard rule wins unless the phase explicitly names itself as an exception.
44
91
 
45
- 1. **Co-pilot stance.** Draft → propose → user approves → tool call → file write. Never write without confirmation.
46
- 2. **Inspect before patching.** Run `dforge_module_inspect` at session start and after every backtrack. Inspect output is read-only; show a summary and continue without asking confirmation for the inspect itself — confirmation applies only to **write** tools.
92
+ 1. **Co-pilot stance.** Draft → propose → user approves → write-tool call → file write. Never write without confirmation. Read-only tools do not need a confirmation gate.
93
+ 2. **Inspect before patching.** Run `dforge_module_inspect` at session start and after every backtrack. Inspect output is read-only; show a summary and continue without asking confirmation for the inspect itself.
47
94
  3. **One thing at a time when interacting with the user.** Applies to:
48
95
  - **Questions.** Ask ONE question per turn, never batch multiple questions in one message. Each subsequent question is informed by prior answers. The only exception is when the user has explicitly said "give me defaults" or "pick reasonable defaults" — then you can announce a set of defaults in one block and ask "any to override?".
49
- - **Entities / views / roles / actions / reports.** Propose ONE per turn. Never batch these. (Field batching inside an entity has a narrow exception in Phase 1.)
96
+ - **Entities / views / roles / actions / reports.** Propose ONE per turn. Never batch these. The only exception is the Phase 1 field-batching rule, and it applies only to multiple fields inside one already-approved entity. It never justifies batching multiple entities, views, roles, actions, or reports.
50
97
  4. **Validate-and-reflect every step.** After every user answer, BEFORE moving to the next question or tool call: restate what you understood in your own words and ask "Right?" or "Does that capture it?". Only proceed once the user confirms. If they correct, repeat the restate-and-confirm loop until aligned. **Goal: zero ambiguity going into the next step.** If you have questions, ask and wait for answers — never proceed with unanswered ones in your head.
51
98
  5. **Tabs in JSON, trailing newline** — tools already emit this; don't reformat.
52
99
  6. **Don't invent fields, codes, roles, or relationships** — they come from the user's domain. If the user said "we have submitters and admins", roles are derived from that; do NOT default to a fixed "admin/contributor/viewer" taxonomy or any other generic set the user didn't ask for.
100
+ 7. **A step is "done" only when its file is written AND reviewed.** Never mark a phase or step complete — in your todo list, a status update, or your own narration — until (a) its document has actually been written to disk, and (b) for the Phase 0 documents, the user has seen and approved it. Deciding what you *would* write is not "done". Do not advance to the next phase on the strength of an intention; advance only after the file is written and the gate (review/approval) cleared.
101
+ 8. **Load before authoring.** Load the matching `dforge://reference/<name>` + `dforge://example/<path>` resources (per the Loading policy table) before scaffolding or modifying any element — fields, views, menus, actions, roles, jobs, etc. — including inside backtracks. They're MCP resources (load by URI, not by disk path). The example files are mandatory structure validators; never work from memory for schema shapes, flags, or column-type patterns.
102
+
103
+ ## Core rules (violations produce invalid modules)
104
+
105
+ Always-on cheat-sheet — enough to author inline; load the linked `references/*.md` for full detail:
106
+
107
+ - **Naming.** `code`, entity `dbObject` keys, column keys all `snake_case`, case-sensitive, entities singular (`opportunity_line`). `code` = DB schema name.
108
+ - **FK + Reference = two columns** (the #1 source of broken modules): hidden FK (`flags: "EM"`, `dbDatatype` = target PK type, no `fieldTypeCd`) **plus** visible Reference (`columnType: "R"`, `fieldTypeCd: "lookup"`, `flags: "VEM"`, `link: {entity, thisKey, otherKey}`), plus the FK in `references`. Never one column that is both. → `column-types.md`
109
+ - **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`
110
+ - **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`
111
+ - **Formula columns** (`columnType: "F"`): `baseDatatypeCd` required, no `dbDatatype`, `flags: "V"`. → `formulas.md`
112
+ - **Roll-up totals** over a child set → Formula `F` with `SUM([set].[field])` (query-time). Never a Generated `G` column aggregating a virtual `F`/`R`/`S` child — its DB trigger reads `OLD.<field>` and install fails (`column old.<field> does not exist`). → `column-types.md`
113
+ - **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`
114
+ - **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`
115
+ - **`toString`:** every entity needs one, `{column}` braces, e.g. `"{first_name} {last_name}"`.
116
+ - **Data views:** `dataSources` array at root — never root-level `entityCode` + `columns`. → `data-views.md`
117
+ - **Menus:** leaf items use `dataViewCode` (not `viewCode`); section nodes omit `itemType`; icons are Bootstrap names sans `bi-`. → `menus.md`
118
+ - **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 `""`. → `security.md`
119
+ - **Action script** in `ui/actions.json` = bare filename (no path, no `.dsl`).
120
+ - **Action DSL dates:** inside `execute:` use lowercase `now()`; `TODAY()`/`NOW()` are formula-only (`canExecute:`/formula columns) and are **undefined in `execute:`**. → `action-dsl.md`
121
+ - **SQL placeholders** = `@paramName` (not `:paramName`).
122
+ - **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`
123
+ - **Seed data:** explicit numeric PKs (`cuid` is `int8`, not a UUID); parents before children via numeric prefix (`01-`, `02-`).
53
124
 
54
125
  ## Tool failure protocol
55
126
 
@@ -67,89 +138,79 @@ Two specific tool errors have known causes worth distinguishing:
67
138
 
68
139
  ## Resume-from-partial-state
69
140
 
70
- At every session start, call `dforge_module_inspect` on the module dir (if it exists).
141
+ 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.
71
142
 
72
- - If the dir doesn't exist or has no `manifest.json` start fresh from Phase 0.
73
- - If it does exist:
74
- 1. Read `_brief/00-intake.md` and `_brief/changelog.md` if present.
75
- 2. Cross-reference the inspect output against the brief to infer the last completed phase (e.g. entities exist + views exist + roles missing → last completed = Phase 3).
76
- 3. 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?"
77
- 4. Wait for the user's answer before proceeding.
143
+ 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.
78
144
 
79
- ## Phase 0 Intake (required)
145
+ - If the dir doesn't exist or has no `manifest.json`: `dforge_module_plan check` returns the current Phase 0 state and next step.
146
+ - If the dir does exist (manifest found):
147
+ 1. Read `_brief/changelog.md` if present.
148
+ 2. Call `dforge_module_inspect` to get entity/view/role inventory.
149
+ 3. Cross-reference entities/views/roles to infer last completed phase.
150
+ 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?"
151
+ 5. Wait for the user's answer before proceeding.
80
152
 
81
- **Preconditions:** none.
153
+ ## Phase 0 — owned by `dforge_module_plan`
82
154
 
83
- **Action:** Walk through the questions below **one at a time, in sequence**. After each answer, apply the validate-and-reflect rule (hard rule #4): restate what you understood, confirm, then proceed to the next question. Each subsequent question is informed by prior answers don't ask Q2 in a way that contradicts what Q1 established. Don't batch.
155
+ 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).
84
156
 
85
- **Interaction style — free-form prose only.** Every question in Phase 0 is asked as a plain-language sentence in your conversation message. Do **NOT** use `AskUserQuestion`, picker UIs, multiple-choice tabs, structured forms, or any tool that presents the user with predefined options to choose from. The whole point of Phase 0 is to elicit the user's own words about purpose, user types, and verbs — predefined buckets bias the answer into your taxonomy and lose the verbs we need for Phase 5. If your client offers a picker tool, suppress it for Phase 0; resume normal tool use in Phase 1+.
157
+ The loop the tool walks you through:
86
158
 
87
- **Forbidden picker examples that have leaked in past sessions** (do not present any variant of these):
88
- - "Single role / Two roles / Three+ roles" — predetermines security shape before entities exist
89
- - "admin / manager / user / viewer" or "admin / contributor / viewer" imposes a generic taxonomy
90
- - "English only / Multilingual" answer in plain text instead
91
-
92
- **Exception:** if the user explicitly says "give me defaults" / "pick reasonable defaults" / similar, you may propose a default brief in one block, restate it, and ask "any to override?". Otherwise, sequential free-form text only.
93
-
94
- **Question order** (use the wording in your own voice):
95
-
96
- 1. **Purpose.** "In one sentence, what does this module do?"
97
- Reflect: "OK — so it's a `<paraphrase>`. Right?" → wait.
159
+ | Sub-phase | You do | Then call |
160
+ |---|---|---|
161
+ | 0a Identity | ask the 5 returned questions one at a time | `write_identity` write the returned `CLAUDE.md` to disk |
162
+ | 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 }` |
163
+ | 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 }` |
164
+ | 0d Validate | `validate` (structural) evaluate the returned `semanticChecks` against the docs (read them from disk) `validate` again with `checkResults` | unlocks `readyToScaffold: true` |
98
165
 
99
- 2. **User types and verbs** capture in plain language. "Who'll use this, and what does each type DO with it?" Listen for verbs that imply actions on data: submits, approves, reviews, issues, receives, matches, closes, etc.
166
+ **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`.
100
167
 
101
- **Capture format full verb-form sentences, not role labels.** Write each as `<descriptor of the person> <verb phrase>`. Never use role-noun labels (Requester, Manager, Buyer, Admin, Approver, Viewer, Contributor, AP Clerk, etc.) as the bullet head those are role NAMES which prematurely commit to a security taxonomy.
168
+ > **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.
102
169
 
103
- Good:
104
- ```
105
- - Anyone in the company submits purchase requests and tracks their own.
106
- - Department managers approve or reject pending requests for their team.
107
- - Buyers in the procurement team manage suppliers, collect quotes, and issue purchase orders.
108
- - Warehouse staff confirm what physically arrived against the PO.
109
- - Accounts payable staff match supplier bills against the PO and receipt, then approve for payment.
110
- ```
170
+ ### 0b intake — guardrails the tool can't enforce
111
171
 
112
- Bad (role labels as headings):
113
- ```
114
- - **Requester** — submits purchase requests
115
- - **Approver** — approves pending requests
116
- - **Buyer** — manages suppliers
117
- - **AP Clerk** — matches bills
118
- ```
119
- The bad form trades situational verbs for fixed nouns and biases Phase 5 toward exactly those roles. Phase 5 might consolidate (e.g. one role covers both warehouse and AP) or split — that's Phase 5's job.
172
+ **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".
120
173
 
121
- Example missing verbs: "admins and users" push back: "What does an admin do that a user can't?"
122
- Reflect: "So users are: `<bullets>`. Right?" → wait.
174
+ **Capture user types as verb-form sentences, never role labels.** Write each as `<descriptor of the person> <verb phrase>`:
123
175
 
124
- **Hard forbidden in Phase 0:** do NOT emit role codes (`<code>.admin`, `<code>.requester`, etc.), do NOT use role-noun labels as bullet heads, do NOT propose a rights matrix, do NOT add a "Target user roles" section to the brief. Roles are derived from entities + verbs in Phase 5, and **entities don't exist yet**.
176
+ Good:
177
+ ```
178
+ - Anyone in the company submits purchase requests and tracks their own.
179
+ - Department managers approve or reject pending requests for their team.
180
+ - Buyers in procurement manage suppliers, collect quotes, and issue purchase orders.
181
+ ```
182
+ ❌ Bad (role-noun headings bias Phase 5 toward exactly those roles):
183
+ ```
184
+ - **Requester** — submits purchase requests
185
+ - **Approver** — approves pending requests
186
+ ```
187
+ 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.
125
188
 
126
- 3. **Existing dForge modules to depend on.** "Are you building this on top of other dForge modules e.g. needing entities from `crm` or `parties`?" (`admin` and `metadata` are platform-implicit. Don't ask about them. Don't even MENTION them in the brief every module depends on them, so listing them in a per-module brief is pure noise.) If the user names actual deps, capture those; if not, the brief's Dependencies section should read literally `None.` or be omitted.
127
- Reflect → wait.
189
+ **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.
128
190
 
129
- 4. **Language scope.** "English only, or any other locales the module needs to ship with translations for?"
130
- Reflect → wait.
191
+ ## Phase 1 Domain (required)
131
192
 
132
- 5. **Optional follow-updomain ambiguities.** If anything in answers 1-4 left an open question (e.g. "what counts as a 'closed' feedback item?", "is the submitter always a logged-in user or also anonymous?"), ask that question now, one at a time. Continue until you can describe how the module should work without any open questions in your head. **Goal of Phase 0: you understand the module well enough to design entities in Phase 1 without further clarification.**
193
+ > **Fast on-rampimporting 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:
194
+ > - **DBML/SQL** → `dforge_dbml_import` (deterministic parser). Pass the DBML text and, for a brand-new module, `module: { code, displayName }`.
195
+ > - **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.)
196
+ > 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.
133
197
 
134
- **Write:** `_brief/00-intake.md` after the final reflection. **Allowed sections (exhaustive):**
135
- - `# <module-name> — intake`
136
- - `## Purpose` (one paragraph)
137
- - `## Module identity` (code, display name, target path)
138
- - `## User types` (bullet list — `<type> — <verbs>`. NO role codes, NO rights, NO "Target user roles" table.)
139
- - `## Dependencies` (which dForge modules)
140
- - `## Languages`
141
- - `## Scope / success criteria` (only if mentioned by user)
142
- - `## Open assumptions` (anything you flagged + need to revisit in Phase 1)
198
+ **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.
143
199
 
144
- **Forbidden sections in the brief:** any roles table, any entity proposal (entities are Phase 1's deliverable, not Phase 0's). If you find yourself drafting a "Target user roles" table stop and replace it with the verb-only bullet list.
200
+ > ⛔ **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.
145
201
 
146
- **Final gate:** Show the brief verbatim, ask "Does this capture everything? Anything to fix or add?". Proceed only on explicit confirmation. The next message you send after confirmation should be the start of Phase 1 proposing the entity inventory.
202
+ **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).
147
203
 
148
- ## Phase 1Domain (required)
204
+ **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:
149
205
 
150
- **Preconditions:** intake brief written and confirmed.
206
+ 1. Every FK in the relationship map has a corresponding field listed for the child entity.
207
+ 2. Every action's `canExecute` guard references a status value that exists in that entity's options list.
208
+ 3. Every seed record's FK references a parent entity that also has seed data (referential integrity in load order).
209
+ 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.
210
+ 5. Any `SUM([set].[field])` formula is flagged as version-dependent.
211
+ 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.
151
212
 
152
- **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).
213
+ Once all checks pass, present a brief summary (entity count, action count) and ask for final confirmation before calling `dforge_module_create`.
153
214
 
154
215
  **Sub-steps:**
155
216
 
@@ -167,7 +228,9 @@ At every session start, call `dforge_module_inspect` on the module dir (if it ex
167
228
 
168
229
  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.
169
230
 
170
- 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; flag in the brief that upstream base-entity changes are the user's responsibility to track.
231
+ 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.
232
+
233
+ **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.
171
234
 
172
235
  **Field batching rule** (the only Phase-1 exception to the hard rule):
173
236
 
@@ -192,21 +255,21 @@ Skip a sub-step entirely if the user has no need for it. Do NOT fabricate behavi
192
255
 
193
256
  ### 2a. Actions — user-triggered
194
257
 
195
- **Load `dforge://docs/dsl`** (the full action DSL reference block structure, all 30 built-in functions, field-access syntax, batch-mode rules, JS subset, common patterns, anti-patterns) before authoring any DSL. `dforge://docs/conventions` is broader module-level guidance and does NOT cover the DSL grammar.
258
+ **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.)
196
259
 
197
260
  Call `dforge_action_add` per action — one at a time — with the full DSL body. Confirm with the user before each call.
198
261
 
199
262
  ### 2b. Triggers — DB-event-driven
200
263
 
201
- **Load `dforge://schema/triggers`** for the shape; also re-read the trigger formula rules in `dforge://docs/dsl` (trigger conditions use the same syntax as `canExecute:`: single-line `[field] op value` formulas).
264
+ 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`.
202
265
 
203
- For each trigger, propose: entity + event + (optional) condition formula + target action + async flag. Use `dforge_trigger_add`. Triggers reference EXISTING actions — make sure the action exists first (Phase 2a or scaffolded admin actions).
266
+ 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.
204
267
 
205
268
  **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.
206
269
 
207
270
  ### 2c. Scheduled jobs — cron-driven
208
271
 
209
- **Load `dforge://schema/jobs`**.
272
+ Load the "scheduled job" row from the Loading policy table (`dforge://reference/jobs` + `dforge://schema/jobs`).
210
273
 
211
274
  Constraints baked into the tool:
212
275
  - 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 `query()` to fetch the records it needs.
@@ -217,7 +280,7 @@ Use `dforge_job_add` per job.
217
280
 
218
281
  ### 2d. Webhooks — outbound HTTP
219
282
 
220
- **Load `dforge://schema/webhooks`**.
283
+ Load the "webhook" row from the Loading policy table (`dforge://schema/webhooks`).
221
284
 
222
285
  For each webhook: entity + event + endpoint URL + (optional) condition + (optional) payload shape (include/exclude/includeOld). Use `dforge_webhook_add`.
223
286
 
@@ -229,6 +292,8 @@ For authenticated endpoints: put bearer tokens / API keys behind `getSecret()` (
229
292
 
230
293
  **Preconditions:** Phase 1 complete.
231
294
 
295
+ **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.
296
+
232
297
  ### 3a. Default grids (required, do FIRST)
233
298
 
234
299
  For every entity in the manifest, call `dforge_view_add` with `viewType: "grid"` and `dataSources: [{ entityCode: <entity>, columns: [...] }]`.
@@ -247,11 +312,11 @@ Propose a specialized view (kanban / calendar / list-with-levels / tree-grid / m
247
312
  - The entity self-references (parent FK to itself) — tree-grid candidate.
248
313
  - The entity has a 1:N detail child with `parentSetField` declared — list-with-levels or master-detail candidate.
249
314
 
250
- If none of these fire, skip specialized views for that entity. Read `dforge://schema/data-views` for the `viewConfig` shape of the type you're proposing before calling `dforge_view_add`.
315
+ 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`.
251
316
 
252
317
  ### 3c. Reports (optional)
253
318
 
254
- 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). Pull `dforge://schema/reports` first.
319
+ 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.
255
320
 
256
321
  **Exit criteria:** every entity has a default grid; every specialized view has a stated trigger; reports cover the stated reporting use cases.
257
322
 
@@ -259,6 +324,8 @@ Add reports only when management aggregation/grouping isn't covered by views. `d
259
324
 
260
325
  **Preconditions:** Phase 3 complete.
261
326
 
327
+ **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.
328
+
262
329
  - **Settings**: `dforge_setting_add` per configurable value the user requested.
263
330
  - **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`.**
264
331
  - **Seed data**: only when the module needs reference data on install.
@@ -267,12 +334,14 @@ Add reports only when management aggregation/grouping isn't covered by views. `d
267
334
 
268
335
  ## Phase 5 — Security
269
336
 
270
- **Preconditions:** Phases 1, 3 complete (you need entity/view/action/report codes to grant rights on).
337
+ **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).
271
338
 
272
339
  ### 5a. Roles + rights matrix (required)
273
340
 
341
+ **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.
342
+
274
343
  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.
275
- 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 "users" 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.
344
+ 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.
276
345
  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.
277
346
  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.
278
347
  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.
@@ -280,6 +349,7 @@ Add reports only when management aggregation/grouping isn't covered by views. `d
280
349
  **Rights semantics** (additive — multiple roles UNION, never revoke):
281
350
  - Entities: any subset of `SIUDC` (Select / Insert / Update / Delete / Clone)
282
351
  - Actions / reports: `E` (Execute), or omit to deny
352
+ - **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.
283
353
 
284
354
  ### 5b. Security folders (optional)
285
355
 
@@ -287,18 +357,77 @@ Only if intake said data must be partitioned per folder (multi-warehouse, multi-
287
357
 
288
358
  If needed: `dforge_folder_add` per sub-folder, passing `entities` with `rowFilter` (SQL string OR canonical `{c,o,v}` / `{g,i:[]}` filter).
289
359
 
290
- **Exit criteria:** every entity is reachable to at least one role; if folders declared, every folder has security mapped.
360
+ **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.
361
+
362
+ > ⛔ **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.
291
363
 
292
364
  ## Phase 6 — Verify (required, non-skippable)
293
365
 
294
- **Preconditions:** all **required** prior phases complete. Optional phases (2, 3c reports, 4 settings/seed, 5b folders) are not preconditions — explicitly skipped optional phases do not block Phase 6.
366
+ **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.
295
367
 
296
368
  **Steps:**
297
369
 
298
- 1. `dforge_module_pack` produces `.dforge` tarball.
370
+ ### Step 1 Automated validation (blocking gate)
371
+
372
+ 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.
373
+
374
+ ### Step 2 — Pre-pack self-review (blocking gate)
375
+
376
+ 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.
377
+
378
+ **Top install-blockers — scan these first** (each is a documented real install failure the platform validator rejects):
379
+
380
+ 1. **DSL dates:** `execute:` blocks use lowercase `now()`; never `TODAY()`/`NOW()` (formula-only → `'TODAY' is not defined`).
381
+ 2. **Roll-ups:** a total over a child set is a Formula (`F`) column with `SUM([set].[field])` — never a Generated (`G`) column aggregating a virtual `F`/`R`/`S` child (→ `db_error: column old.<field> does not exist`).
382
+ 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 `""`.
383
+ 4. **Manifest:** no `translations` key (translation files auto-discovered; non-English locales → `supportedLocales`).
384
+ 5. **Column defaults:** set via `formula` / `numberSequence` / DSL — never a `defaultValue` key on an entity field.
385
+ 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`).
386
+
387
+ Key areas (full checklist):
388
+
389
+ - **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`.
390
+ - **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).
391
+ - **Formula columns** (`columnType: "F"`): have `baseDatatypeCd`, no `dbDatatype`, `flags: "V"`.
392
+ - **Flags**: only `V`, `I`, `E`, `M`, `H` used — no `U`, `S`, or `P`.
393
+ - **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}]`).
394
+ - **Menus**: leaf items have `dataViewCode` (not `viewCode`); icons are Bootstrap names without the `bi-` prefix; section nodes omit `itemType`.
395
+ - **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`.
396
+ - **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`.
397
+ - **Seed data**: numeric PKs; parent entities loaded before children; no circular references.
398
+ - **Translations**: a `translations/<locale>.json` file exists for every locale in `supportedLocales`; every trait-provided field (`created_at`, `updated_at`, etc.) has a translation entry in each file.
399
+
400
+ ### Step 3 — Translation deferral check
401
+
402
+ 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.
403
+
404
+ ### Step 4 — Final inspect + version audit
405
+
406
+ Run `dforge_module_inspect`. Show a one-line summary: entity count, view count, action count, role count. Then confirm version strings with the user:
407
+
408
+ - **`version`**: always bump (semver) before packing.
409
+ - **`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.
410
+
411
+ Get user confirmation on both version strings before packing.
412
+
413
+ ### Step 5 — Pack + install
414
+
415
+ 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.)
299
416
  2. `dforge_module_install` with `DFORGE_URL` / `DFORGE_TOKEN`. Runs the full server-side validator.
300
- 3. **If install fails on a module defect** (schema, FK, missing translation, etc.), the error message tells you which phase to backtrack to. Use the backtrack protocol.
301
- 4. **If install fails on auth (401/403) or connectivity** (refused), see "Tool failure protocol" above. Do not backtrack fix credentials.
417
+
418
+ **If install fails on a module defect**, use this table to identify which phase to backtrack to, then apply the backtrack protocol, fix, re-run Steps 1–2 (validate + self-review), and re-pack:
419
+
420
+ | Install error pattern | Backtrack to |
421
+ |---|---|
422
+ | "unknown entity code" or "unknown view code" | Phase 1 or 3 |
423
+ | "missing translation key" | Phase 4 |
424
+ | "FK constraint violation in seed data" | Phase 1 (check seed data load order) |
425
+ | "role right granted on unknown object" | Phase 5 |
426
+ | "action script not found" | Phase 2a |
427
+ | "formula compile error" | Phase 1 (field def) or Phase 2a (DSL) |
428
+ | "duplicate code" | Phase where the duplicate was introduced |
429
+
430
+ **If install fails on auth (401/403) or connectivity** (refused), see "Tool failure protocol" above. Do not backtrack — fix credentials.
302
431
 
303
432
  **Exit criteria:** install exits 0 against a real tenant.
304
433
 
@@ -312,16 +441,19 @@ When a later phase exposes a problem in an earlier phase, follow steps 1–6 IN
312
441
  2. **Name the issue precisely.** "Phase 3 wants a kanban grouped by `lead_status`, but Phase 1 didn't define `lead_status` on entity `lead`."
313
442
  3. **Identify the target phase + decision.** "Backtrack to Phase 1: add field `lead_status` to entity `lead`."
314
443
  4. **Get user sign-off.** Describe the change including any cascading impacts.
315
- 5. **Apply the smallest tool that fits.** This rule overrides the "typical phase" labels in the tool reference table. Prefer `entity_field_add` over `entity_add`; `role_right_set` over `role_add`; `view_modify` over `view_add` + remove.
444
+ 5. **Apply the smallest tool that fits.** This rule overrides the "typical phase" labels in the tool reference table. Prefer `entity_field_add` over `entity_add`; `entity_field_rename` over remove+add when renaming (it propagates references for you); `role_right_set` over `role_add`; `view_modify` over `view_add` + remove.
316
445
  6. **Run `dforge_module_inspect` again** to surface knock-on impacts. Fix in order. Resume the original phase.
317
446
 
318
- **Entity rename or deletion specifically requires cascade discovery:**
447
+ **Field and entity rename/delete are handled for you** by the refactor-safe tools, which propagate the cascade in one call:
448
+
449
+ - **Rename a field** → `dforge_entity_field_rename` (paired Reference, formulas, views, seed, cross-entity FKs).
450
+ - **Remove a field** → `dforge_entity_field_remove` (cascade-cleans views/seed/paired Reference; warns formula/cross-entity).
451
+ - **Rename an entity** → `dforge_entity_rename` (PK `{old}_id → {new}_id`, link.entity, references, view entityCode, role keys, action entity, folder bindings, seed).
452
+ - **Delete an entity** → `dforge_entity_delete` (file + seed + manifest + roles + folders + view sources).
453
+
454
+ **Always apply the response's `deletes` array as well as `files`** — rename/delete move or drop files, and `deletes` lists the module-root-relative paths to remove. Each tool surfaces what it could NOT auto-fix (reports datasets, translations, menu labels, DSL bodies, dangling cross-entity FKs) in `warning` — address those by hand. **After any rename/delete, run `dforge_module_validate`** and fix anything that still dangles before resuming.
319
455
 
320
- Before applying:
321
- 1. Run `dforge_module_inspect` to enumerate every reference: views' `dataSources.entityCode`, role `rights` keys, action `entity`, report dataset `entityCd`, seed-data files, formula/DSL bodies.
322
- 2. List every affected artifact to the user. Require explicit confirmation.
323
- 3. Apply in **reverse dependency order**: roles → reports → views → actions → entity itself.
324
- 4. Re-inspect; verify no dangling references remain.
456
+ If you must do an entity-level change by hand (e.g. a surface a tool doesn't cover): enumerate references with `dforge_module_inspect`, confirm with the user, apply in reverse dependency order (roles → reports → views → actions → entity itself), then `dforge_module_validate`.
325
457
 
326
458
  **After every backtrack** append to `_brief/changelog.md`:
327
459
 
@@ -0,0 +1,38 @@
1
+ # Simple Todo — Example Module
2
+
3
+ A minimal but complete dForge module. Use it as a reference for:
4
+
5
+ - Module package layout
6
+ - Entity definition with traits, columns, and a reference
7
+ - Data view with a grid
8
+ - Menu with a single leaf item
9
+ - Security role with full CRUD
10
+ - Seed data with numbered files
11
+ - A simple DSL action (mark as done)
12
+
13
+ **Not meant for production** — it's a teaching example.
14
+
15
+ ## What it contains
16
+
17
+ | File | Purpose |
18
+ |---|---|
19
+ | `manifest.json` | Module metadata and file listing |
20
+ | `entities/todo_list.json` | Lists that group todos |
21
+ | `entities/todo_item.json` | Individual todo items, with reference to list |
22
+ | `ui/data_views.json` | Two grid views — one for lists, one for items |
23
+ | `ui/menus.json` | Nested menu with "Lists" and "Items" |
24
+ | `ui/actions.json` | Registers the mark-done action |
25
+ | `logic/actions/mark_done.dsl` | The action DSL |
26
+ | `security/roles.json` | `todo_user` role with full CRUD |
27
+ | `seed-data/01-lists.json` | Two seed lists |
28
+
29
+ ## Key patterns demonstrated
30
+
31
+ 1. **FK+Reference pattern** — `todo_item` has `list_id` (hidden FK) + `list` (visible Reference)
32
+ 2. **Traits** — both entities use `["identity", "audit"]`
33
+ 3. **toString** — `todo_list` uses `{name}`, `todo_item` uses `{title}`
34
+ 4. **`dataSources` array** in data views (never root-level `entityCode`)
35
+ 5. **Nested dict menus** with `dataViewCode` on leaves
36
+ 6. **`rights` (not `entityRights`)** in roles
37
+ 7. **Numbered seed files** for FK ordering
38
+ 8. **DSL action** with `canExecute:` gating on `[done] == false`