@dforge-core/dforge-mcp 0.1.0-rc.9 → 0.1.1
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 +132 -0
- package/README.md +84 -27
- package/dist/server.js +2957 -616
- package/docs/creating-modules.md +11 -4
- package/package.json +11 -6
- package/resources/docs/conventions.md +22 -16
- package/resources/docs/dsl-reference.md +767 -0
- package/resources/schemas/entity.schema.json +8 -1
- package/resources/schemas/print-templates.schema.json +82 -0
- package/resources/schemas/reports.schema.json +4 -0
- package/resources/schemas/triggers.schema.json +59 -0
- package/skills/dforge-mcp-author/SKILL.md +269 -69
- package/skills/dforge-mcp-author/examples/matrix-budget/README.md +43 -0
- package/skills/dforge-mcp-author/examples/matrix-budget/entities/budget_category.json +24 -0
- package/skills/dforge-mcp-author/examples/matrix-budget/entities/budget_line.json +56 -0
- package/skills/dforge-mcp-author/examples/matrix-budget/manifest.json +30 -0
- package/skills/dforge-mcp-author/examples/matrix-budget/security/roles.json +9 -0
- package/skills/dforge-mcp-author/examples/matrix-budget/seed-data/01-categories.json +8 -0
- package/skills/dforge-mcp-author/examples/matrix-budget/ui/data_views.json +42 -0
- package/skills/dforge-mcp-author/examples/simple-todo/README.md +38 -0
- package/skills/dforge-mcp-author/examples/simple-todo/entities/todo_item.json +83 -0
- package/skills/dforge-mcp-author/examples/simple-todo/entities/todo_list.json +43 -0
- package/skills/dforge-mcp-author/examples/simple-todo/logic/actions/mark_done.dsl +6 -0
- package/skills/dforge-mcp-author/examples/simple-todo/manifest.json +32 -0
- package/skills/dforge-mcp-author/examples/simple-todo/security/roles.json +10 -0
- package/skills/dforge-mcp-author/examples/simple-todo/seed-data/01-lists.json +17 -0
- package/skills/dforge-mcp-author/examples/simple-todo/ui/actions.json +11 -0
- package/skills/dforge-mcp-author/examples/simple-todo/ui/data_views.json +35 -0
- package/skills/dforge-mcp-author/examples/simple-todo/ui/menus.json +28 -0
- package/skills/dforge-mcp-author/references/action-dsl.md +397 -0
- package/skills/dforge-mcp-author/references/column-types.md +168 -0
- package/skills/dforge-mcp-author/references/conventions.md +177 -0
- package/skills/dforge-mcp-author/references/data-migration.md +270 -0
- package/skills/dforge-mcp-author/references/data-views.md +243 -0
- package/skills/dforge-mcp-author/references/excel-import.md +61 -0
- package/skills/dforge-mcp-author/references/field-types.md +144 -0
- package/skills/dforge-mcp-author/references/filters.md +326 -0
- package/skills/dforge-mcp-author/references/flags.md +73 -0
- package/skills/dforge-mcp-author/references/formulas.md +206 -0
- package/skills/dforge-mcp-author/references/jobs.md +149 -0
- package/skills/dforge-mcp-author/references/manifest.md +123 -0
- package/skills/dforge-mcp-author/references/menus.md +164 -0
- package/skills/dforge-mcp-author/references/number-sequences.md +117 -0
- package/skills/dforge-mcp-author/references/print-templates.md +159 -0
- package/skills/dforge-mcp-author/references/queries.md +312 -0
- package/skills/dforge-mcp-author/references/reports.md +398 -0
- package/skills/dforge-mcp-author/references/schema-import.md +331 -0
- package/skills/dforge-mcp-author/references/security.md +244 -0
- package/skills/dforge-mcp-author/references/settings.md +120 -0
- package/skills/dforge-mcp-author/references/traits.md +153 -0
- package/skills/dforge-mcp-author/references/translations.md +158 -0
- package/skills/dforge-mcp-author/references/validation-checklist.md +183 -0
- 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
|
|
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
|
-
| `
|
|
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
|
-
| `
|
|
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,80 @@ 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
|
-
|
|
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
|
|
35
54
|
|
|
36
|
-
-
|
|
37
|
-
- `dforge://schema/manifest`, `entity`, `data-views`, `folders`, `menus`, `roles`, `reports`, `settings`, `jobs`, `seed-data` — consult before emitting each file kind
|
|
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.
|
|
38
56
|
|
|
39
|
-
**
|
|
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 matrix (pivot) view | `dforge://reference/data-views` (§Matrix), `dforge://example/matrix-budget/ui/data_views.json`, `dforge://example/matrix-budget/entities/budget_line.json` | — |
|
|
71
|
+
| Add a specialized view (kanban / calendar / tree-grid / master-detail) | `dforge://reference/data-views` | `dforge://schema/data-views` (viewConfig shape) |
|
|
72
|
+
| Add a menu | `dforge://reference/menus`, `dforge://example/ui/menus.json` | — |
|
|
73
|
+
| 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) |
|
|
74
|
+
| Add a trigger (DB-event → action) | — (no dedicated reference) | `dforge://schema/triggers`; trigger-condition syntax in `dforge://docs/dsl` |
|
|
75
|
+
| Add a scheduled job | `dforge://reference/jobs` | `dforge://schema/jobs` |
|
|
76
|
+
| Add a webhook (outbound HTTP) | — (no dedicated reference) | `dforge://schema/webhooks` |
|
|
77
|
+
| Add filters (views, folders, reports) | `dforge://reference/filters` | — |
|
|
78
|
+
| Add security roles or folders | `dforge://reference/security`, `dforge://reference/filters`, `dforge://example/security/roles.json` | — |
|
|
79
|
+
| Add a report | `dforge://reference/reports` | `dforge://schema/reports` |
|
|
80
|
+
| Add a print template | `dforge://reference/print-templates` | — |
|
|
81
|
+
| Add translations | `dforge://reference/translations` | — |
|
|
82
|
+
| Add a number sequence | `dforge://reference/number-sequences` | — |
|
|
83
|
+
| Add module settings | `dforge://reference/settings` | — |
|
|
84
|
+
| Add pre-built saved queries | `dforge://reference/queries` | — |
|
|
85
|
+
| Import from DBML/SQL | `dforge://reference/schema-import` | — |
|
|
86
|
+
| Migrate from a legacy database | `dforge://reference/data-migration` | — |
|
|
87
|
+
| Final pre-pack validation | `dforge://reference/validation-checklist` | — |
|
|
40
88
|
|
|
41
89
|
## Hard rules
|
|
42
90
|
|
|
43
91
|
These are absolute. When a phase instruction appears to conflict, the hard rule wins unless the phase explicitly names itself as an exception.
|
|
44
92
|
|
|
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
|
|
93
|
+
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.
|
|
94
|
+
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
95
|
3. **One thing at a time when interacting with the user.** Applies to:
|
|
48
96
|
- **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.
|
|
97
|
+
- **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
98
|
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
99
|
5. **Tabs in JSON, trailing newline** — tools already emit this; don't reformat.
|
|
52
100
|
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.
|
|
101
|
+
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.
|
|
102
|
+
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.
|
|
103
|
+
|
|
104
|
+
## Core rules (violations produce invalid modules)
|
|
105
|
+
|
|
106
|
+
Always-on cheat-sheet — enough to author inline; load the linked `references/*.md` for full detail:
|
|
107
|
+
|
|
108
|
+
- **Naming.** `code`, entity `dbObject` keys, column keys all `snake_case`, case-sensitive, entities singular (`opportunity_line`). `code` = DB schema name.
|
|
109
|
+
- **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`
|
|
110
|
+
- **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`
|
|
111
|
+
- **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`
|
|
112
|
+
- **Formula columns** (`columnType: "F"`): `baseDatatypeCd` required, no `dbDatatype`, `flags: "V"`. → `formulas.md`
|
|
113
|
+
- **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`
|
|
114
|
+
- **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`
|
|
115
|
+
- **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`
|
|
116
|
+
- **`toString`:** every entity needs one, `{column}` braces, e.g. `"{first_name} {last_name}"`.
|
|
117
|
+
- **Data views:** `dataSources` array at root — never root-level `entityCode` + `columns`. → `data-views.md`
|
|
118
|
+
- **Menus:** leaf items use `dataViewCode` (not `viewCode`); section nodes omit `itemType`; icons are Bootstrap names sans `bi-`. → `menus.md`
|
|
119
|
+
- **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`
|
|
120
|
+
- **Action script** in `ui/actions.json` = bare filename (no path, no `.dsl`).
|
|
121
|
+
- **Action DSL dates:** inside `execute:` use lowercase `now()`; `TODAY()`/`NOW()` are formula-only (`canExecute:`/formula columns) and are **undefined in `execute:`**. → `action-dsl.md`
|
|
122
|
+
- **SQL placeholders** = `@paramName` (not `:paramName`).
|
|
123
|
+
- **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`
|
|
124
|
+
- **Seed data:** explicit numeric PKs (`cuid` is `int8`, not a UUID); parents before children via numeric prefix (`01-`, `02-`).
|
|
53
125
|
|
|
54
126
|
## Tool failure protocol
|
|
55
127
|
|
|
@@ -67,63 +139,80 @@ Two specific tool errors have known causes worth distinguishing:
|
|
|
67
139
|
|
|
68
140
|
## Resume-from-partial-state
|
|
69
141
|
|
|
70
|
-
At every session start, call `dforge_module_inspect` on the module dir (if
|
|
142
|
+
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
143
|
|
|
72
|
-
|
|
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.
|
|
144
|
+
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
145
|
|
|
79
|
-
|
|
146
|
+
- If the dir doesn't exist or has no `manifest.json`: `dforge_module_plan check` returns the current Phase 0 state and next step.
|
|
147
|
+
- If the dir does exist (manifest found):
|
|
148
|
+
1. Read `_brief/changelog.md` if present.
|
|
149
|
+
2. Call `dforge_module_inspect` to get entity/view/role inventory.
|
|
150
|
+
3. Cross-reference entities/views/roles to infer last completed phase.
|
|
151
|
+
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?"
|
|
152
|
+
5. Wait for the user's answer before proceeding.
|
|
80
153
|
|
|
81
|
-
|
|
154
|
+
## Phase 0 — owned by `dforge_module_plan`
|
|
82
155
|
|
|
83
|
-
|
|
156
|
+
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
157
|
|
|
85
|
-
|
|
158
|
+
The loop the tool walks you through:
|
|
86
159
|
|
|
87
|
-
|
|
160
|
+
| Sub-phase | You do | Then call |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| 0a Identity | ask the 5 returned questions one at a time | `write_identity` → write the returned `CLAUDE.md` to disk |
|
|
163
|
+
| 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 }` |
|
|
164
|
+
| 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 }` |
|
|
165
|
+
| 0d Validate | `validate` (structural) → evaluate the returned `semanticChecks` against the docs (read them from disk) → `validate` again with `checkResults` | unlocks `readyToScaffold: true` |
|
|
88
166
|
|
|
89
|
-
1.
|
|
90
|
-
Reflect: "OK — so it's a `<paraphrase>`. Right?" → wait.
|
|
167
|
+
**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`.
|
|
91
168
|
|
|
92
|
-
|
|
93
|
-
Example good answer: "any logged-in user submits, admins triage and respond." Capture as a bullet list of `<user type> — <verbs>`.
|
|
94
|
-
Example bad coverage: just "admins and users" with no verbs — push back: "What does an admin do that a user can't?"
|
|
95
|
-
Reflect: "So users are: `<bullets>`. Right?" → wait.
|
|
169
|
+
> **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.
|
|
96
170
|
|
|
97
|
-
|
|
171
|
+
### 0b intake — guardrails the tool can't enforce
|
|
98
172
|
|
|
99
|
-
|
|
100
|
-
Reflect → wait.
|
|
173
|
+
**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".
|
|
101
174
|
|
|
102
|
-
|
|
103
|
-
Reflect → wait.
|
|
175
|
+
**Capture user types as verb-form sentences, never role labels.** Write each as `<descriptor of the person> <verb phrase>`:
|
|
104
176
|
|
|
105
|
-
|
|
177
|
+
✅ Good:
|
|
178
|
+
```
|
|
179
|
+
- Anyone in the company submits purchase requests and tracks their own.
|
|
180
|
+
- Department managers approve or reject pending requests for their team.
|
|
181
|
+
- Buyers in procurement manage suppliers, collect quotes, and issue purchase orders.
|
|
182
|
+
```
|
|
183
|
+
❌ Bad (role-noun headings bias Phase 5 toward exactly those roles):
|
|
184
|
+
```
|
|
185
|
+
- **Requester** — submits purchase requests
|
|
186
|
+
- **Approver** — approves pending requests
|
|
187
|
+
```
|
|
188
|
+
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.
|
|
106
189
|
|
|
107
|
-
**
|
|
108
|
-
- `# <module-name> — intake`
|
|
109
|
-
- `## Purpose` (one paragraph)
|
|
110
|
-
- `## Module identity` (code, display name, target path)
|
|
111
|
-
- `## User types` (bullet list — `<type> — <verbs>`. NO role codes, NO rights, NO "Target user roles" table.)
|
|
112
|
-
- `## Dependencies` (which dForge modules)
|
|
113
|
-
- `## Languages`
|
|
114
|
-
- `## Scope / success criteria` (only if mentioned by user)
|
|
115
|
-
- `## Open assumptions` (anything you flagged + need to revisit in Phase 1)
|
|
190
|
+
**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.
|
|
116
191
|
|
|
117
|
-
|
|
192
|
+
## Phase 1 — Domain (required)
|
|
118
193
|
|
|
119
|
-
**
|
|
194
|
+
> **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:
|
|
195
|
+
> - **DBML/SQL** → `dforge_dbml_import` (deterministic parser). Pass the DBML text and, for a brand-new module, `module: { code, displayName }`.
|
|
196
|
+
> - **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.)
|
|
197
|
+
> 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.
|
|
120
198
|
|
|
121
|
-
|
|
199
|
+
**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.
|
|
122
200
|
|
|
123
|
-
**
|
|
201
|
+
> ⛔ **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.
|
|
124
202
|
|
|
125
203
|
**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).
|
|
126
204
|
|
|
205
|
+
**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:
|
|
206
|
+
|
|
207
|
+
1. Every FK in the relationship map has a corresponding field listed for the child entity.
|
|
208
|
+
2. Every action's `canExecute` guard references a status value that exists in that entity's options list.
|
|
209
|
+
3. Every seed record's FK references a parent entity that also has seed data (referential integrity in load order).
|
|
210
|
+
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.
|
|
211
|
+
5. Any `SUM([set].[field])` formula is flagged as version-dependent.
|
|
212
|
+
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.
|
|
213
|
+
|
|
214
|
+
Once all checks pass, present a brief summary (entity count, action count) and ask for final confirmation before calling `dforge_module_create`.
|
|
215
|
+
|
|
127
216
|
**Sub-steps:**
|
|
128
217
|
|
|
129
218
|
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:
|
|
@@ -140,7 +229,9 @@ At every session start, call `dforge_module_inspect` on the module dir (if it ex
|
|
|
140
229
|
|
|
141
230
|
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.
|
|
142
231
|
|
|
143
|
-
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;
|
|
232
|
+
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.
|
|
233
|
+
|
|
234
|
+
**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.
|
|
144
235
|
|
|
145
236
|
**Field batching rule** (the only Phase-1 exception to the hard rule):
|
|
146
237
|
|
|
@@ -148,20 +239,62 @@ A field is **batchable** only if ALL of these are true: scalar primitive (string
|
|
|
148
239
|
|
|
149
240
|
**Exit criteria:** every entity has at least PK + audit traits + 1 user-visible field; FK references resolve; manifest's `entities` map reflects reality.
|
|
150
241
|
|
|
151
|
-
## Phase 2 —
|
|
242
|
+
## Phase 2 — Behavior (optional sub-steps)
|
|
152
243
|
|
|
153
244
|
**Preconditions:** Phase 1 complete.
|
|
154
245
|
|
|
155
|
-
|
|
246
|
+
Phase 2 covers four kinds of behavior — all optional, all individually skip-able. Each fires action logic, but the **trigger** differs:
|
|
247
|
+
|
|
248
|
+
| Sub-step | Fires when | File | Tool | Use when |
|
|
249
|
+
|---|---|---|---|---|
|
|
250
|
+
| 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 |
|
|
251
|
+
| 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 |
|
|
252
|
+
| 2c Scheduled jobs | cron timer | `logic/jobs.json` | `dforge_job_add` | periodic work: nightly cleanup, daily summary, hourly poll |
|
|
253
|
+
| 2d Webhooks | DB event happens → POSTs to external URL | `logic/webhooks.json` | `dforge_webhook_add` | integrations: Slack notifications, Zapier, external dashboards, audit log shipping |
|
|
254
|
+
|
|
255
|
+
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.
|
|
256
|
+
|
|
257
|
+
### 2a. Actions — user-triggered
|
|
258
|
+
|
|
259
|
+
**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.)
|
|
260
|
+
|
|
261
|
+
Call `dforge_action_add` per action — one at a time — with the full DSL body. Confirm with the user before each call.
|
|
262
|
+
|
|
263
|
+
### 2b. Triggers — DB-event-driven
|
|
264
|
+
|
|
265
|
+
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`.
|
|
266
|
+
|
|
267
|
+
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.
|
|
268
|
+
|
|
269
|
+
**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.
|
|
270
|
+
|
|
271
|
+
### 2c. Scheduled jobs — cron-driven
|
|
272
|
+
|
|
273
|
+
Load the "scheduled job" row from the Loading policy table (`dforge://reference/jobs` + `dforge://schema/jobs`).
|
|
274
|
+
|
|
275
|
+
Constraints baked into the tool:
|
|
276
|
+
- 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.
|
|
277
|
+
- `timeout` is required, ≤ 3600s.
|
|
278
|
+
- If `timeout > 300`, you MUST set `jobClass: 'long_running'`.
|
|
156
279
|
|
|
157
|
-
|
|
280
|
+
Use `dforge_job_add` per job.
|
|
158
281
|
|
|
159
|
-
|
|
282
|
+
### 2d. Webhooks — outbound HTTP
|
|
283
|
+
|
|
284
|
+
Load the "webhook" row from the Loading policy table (`dforge://schema/webhooks`).
|
|
285
|
+
|
|
286
|
+
For each webhook: entity + event + endpoint URL + (optional) condition + (optional) payload shape (include/exclude/includeOld). Use `dforge_webhook_add`.
|
|
287
|
+
|
|
288
|
+
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.
|
|
289
|
+
|
|
290
|
+
**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.
|
|
160
291
|
|
|
161
292
|
## Phase 3 — Views (required) + Reports (optional)
|
|
162
293
|
|
|
163
294
|
**Preconditions:** Phase 1 complete.
|
|
164
295
|
|
|
296
|
+
**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.
|
|
297
|
+
|
|
165
298
|
### 3a. Default grids (required, do FIRST)
|
|
166
299
|
|
|
167
300
|
For every entity in the manifest, call `dforge_view_add` with `viewType: "grid"` and `dataSources: [{ entityCode: <entity>, columns: [...] }]`.
|
|
@@ -180,11 +313,11 @@ Propose a specialized view (kanban / calendar / list-with-levels / tree-grid / m
|
|
|
180
313
|
- The entity self-references (parent FK to itself) — tree-grid candidate.
|
|
181
314
|
- The entity has a 1:N detail child with `parentSetField` declared — list-with-levels or master-detail candidate.
|
|
182
315
|
|
|
183
|
-
If none of these fire, skip specialized views for that entity.
|
|
316
|
+
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`.
|
|
184
317
|
|
|
185
318
|
### 3c. Reports (optional)
|
|
186
319
|
|
|
187
|
-
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).
|
|
320
|
+
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.
|
|
188
321
|
|
|
189
322
|
**Exit criteria:** every entity has a default grid; every specialized view has a stated trigger; reports cover the stated reporting use cases.
|
|
190
323
|
|
|
@@ -192,6 +325,8 @@ Add reports only when management aggregation/grouping isn't covered by views. `d
|
|
|
192
325
|
|
|
193
326
|
**Preconditions:** Phase 3 complete.
|
|
194
327
|
|
|
328
|
+
**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.
|
|
329
|
+
|
|
195
330
|
- **Settings**: `dforge_setting_add` per configurable value the user requested.
|
|
196
331
|
- **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`.**
|
|
197
332
|
- **Seed data**: only when the module needs reference data on install.
|
|
@@ -200,12 +335,14 @@ Add reports only when management aggregation/grouping isn't covered by views. `d
|
|
|
200
335
|
|
|
201
336
|
## Phase 5 — Security
|
|
202
337
|
|
|
203
|
-
**Preconditions:** Phases 1
|
|
338
|
+
**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).
|
|
204
339
|
|
|
205
340
|
### 5a. Roles + rights matrix (required)
|
|
206
341
|
|
|
342
|
+
**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.
|
|
343
|
+
|
|
207
344
|
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.
|
|
208
|
-
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
|
|
345
|
+
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.
|
|
209
346
|
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.
|
|
210
347
|
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.
|
|
211
348
|
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.
|
|
@@ -213,6 +350,7 @@ Add reports only when management aggregation/grouping isn't covered by views. `d
|
|
|
213
350
|
**Rights semantics** (additive — multiple roles UNION, never revoke):
|
|
214
351
|
- Entities: any subset of `SIUDC` (Select / Insert / Update / Delete / Clone)
|
|
215
352
|
- Actions / reports: `E` (Execute), or omit to deny
|
|
353
|
+
- **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.
|
|
216
354
|
|
|
217
355
|
### 5b. Security folders (optional)
|
|
218
356
|
|
|
@@ -220,18 +358,77 @@ Only if intake said data must be partitioned per folder (multi-warehouse, multi-
|
|
|
220
358
|
|
|
221
359
|
If needed: `dforge_folder_add` per sub-folder, passing `entities` with `rowFilter` (SQL string OR canonical `{c,o,v}` / `{g,i:[]}` filter).
|
|
222
360
|
|
|
223
|
-
**Exit criteria:** every entity
|
|
361
|
+
**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.
|
|
362
|
+
|
|
363
|
+
> ⛔ **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.
|
|
224
364
|
|
|
225
365
|
## Phase 6 — Verify (required, non-skippable)
|
|
226
366
|
|
|
227
|
-
**Preconditions:** all
|
|
367
|
+
**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.
|
|
228
368
|
|
|
229
369
|
**Steps:**
|
|
230
370
|
|
|
231
|
-
1
|
|
371
|
+
### Step 1 — Automated validation (blocking gate)
|
|
372
|
+
|
|
373
|
+
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.
|
|
374
|
+
|
|
375
|
+
### Step 2 — Pre-pack self-review (blocking gate)
|
|
376
|
+
|
|
377
|
+
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.
|
|
378
|
+
|
|
379
|
+
**Top install-blockers — scan these first** (each is a documented real install failure the platform validator rejects):
|
|
380
|
+
|
|
381
|
+
1. **DSL dates:** `execute:` blocks use lowercase `now()`; never `TODAY()`/`NOW()` (formula-only → `'TODAY' is not defined`).
|
|
382
|
+
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`).
|
|
383
|
+
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 `""`.
|
|
384
|
+
4. **Manifest:** no `translations` key (translation files auto-discovered; non-English locales → `supportedLocales`).
|
|
385
|
+
5. **Column defaults:** set via `formula` / `numberSequence` / DSL — never a `defaultValue` key on an entity field.
|
|
386
|
+
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`).
|
|
387
|
+
|
|
388
|
+
Key areas (full checklist):
|
|
389
|
+
|
|
390
|
+
- **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`.
|
|
391
|
+
- **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).
|
|
392
|
+
- **Formula columns** (`columnType: "F"`): have `baseDatatypeCd`, no `dbDatatype`, `flags: "V"`.
|
|
393
|
+
- **Flags**: only `V`, `I`, `E`, `M`, `H` used — no `U`, `S`, or `P`.
|
|
394
|
+
- **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}]`).
|
|
395
|
+
- **Menus**: leaf items have `dataViewCode` (not `viewCode`); icons are Bootstrap names without the `bi-` prefix; section nodes omit `itemType`.
|
|
396
|
+
- **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`.
|
|
397
|
+
- **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`.
|
|
398
|
+
- **Seed data**: numeric PKs; parent entities loaded before children; no circular references.
|
|
399
|
+
- **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.
|
|
400
|
+
|
|
401
|
+
### Step 3 — Translation deferral check
|
|
402
|
+
|
|
403
|
+
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.
|
|
404
|
+
|
|
405
|
+
### Step 4 — Final inspect + version audit
|
|
406
|
+
|
|
407
|
+
Run `dforge_module_inspect`. Show a one-line summary: entity count, view count, action count, role count. Then confirm version strings with the user:
|
|
408
|
+
|
|
409
|
+
- **`version`**: always bump (semver) before packing.
|
|
410
|
+
- **`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.
|
|
411
|
+
|
|
412
|
+
Get user confirmation on both version strings before packing.
|
|
413
|
+
|
|
414
|
+
### Step 5 — Pack + install
|
|
415
|
+
|
|
416
|
+
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.)
|
|
232
417
|
2. `dforge_module_install` with `DFORGE_URL` / `DFORGE_TOKEN`. Runs the full server-side validator.
|
|
233
|
-
|
|
234
|
-
|
|
418
|
+
|
|
419
|
+
**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:
|
|
420
|
+
|
|
421
|
+
| Install error pattern | Backtrack to |
|
|
422
|
+
|---|---|
|
|
423
|
+
| "unknown entity code" or "unknown view code" | Phase 1 or 3 |
|
|
424
|
+
| "missing translation key" | Phase 4 |
|
|
425
|
+
| "FK constraint violation in seed data" | Phase 1 (check seed data load order) |
|
|
426
|
+
| "role right granted on unknown object" | Phase 5 |
|
|
427
|
+
| "action script not found" | Phase 2a |
|
|
428
|
+
| "formula compile error" | Phase 1 (field def) or Phase 2a (DSL) |
|
|
429
|
+
| "duplicate code" | Phase where the duplicate was introduced |
|
|
430
|
+
|
|
431
|
+
**If install fails on auth (401/403) or connectivity** (refused), see "Tool failure protocol" above. Do not backtrack — fix credentials.
|
|
235
432
|
|
|
236
433
|
**Exit criteria:** install exits 0 against a real tenant.
|
|
237
434
|
|
|
@@ -245,16 +442,19 @@ When a later phase exposes a problem in an earlier phase, follow steps 1–6 IN
|
|
|
245
442
|
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`."
|
|
246
443
|
3. **Identify the target phase + decision.** "Backtrack to Phase 1: add field `lead_status` to entity `lead`."
|
|
247
444
|
4. **Get user sign-off.** Describe the change including any cascading impacts.
|
|
248
|
-
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.
|
|
445
|
+
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.
|
|
249
446
|
6. **Run `dforge_module_inspect` again** to surface knock-on impacts. Fix in order. Resume the original phase.
|
|
250
447
|
|
|
251
|
-
**
|
|
448
|
+
**Field and entity rename/delete are handled for you** by the refactor-safe tools, which propagate the cascade in one call:
|
|
449
|
+
|
|
450
|
+
- **Rename a field** → `dforge_entity_field_rename` (paired Reference, formulas, views, seed, cross-entity FKs).
|
|
451
|
+
- **Remove a field** → `dforge_entity_field_remove` (cascade-cleans views/seed/paired Reference; warns formula/cross-entity).
|
|
452
|
+
- **Rename an entity** → `dforge_entity_rename` (PK `{old}_id → {new}_id`, link.entity, references, view entityCode, role keys, action entity, folder bindings, seed).
|
|
453
|
+
- **Delete an entity** → `dforge_entity_delete` (file + seed + manifest + roles + folders + view sources).
|
|
454
|
+
|
|
455
|
+
**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.
|
|
252
456
|
|
|
253
|
-
|
|
254
|
-
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.
|
|
255
|
-
2. List every affected artifact to the user. Require explicit confirmation.
|
|
256
|
-
3. Apply in **reverse dependency order**: roles → reports → views → actions → entity itself.
|
|
257
|
-
4. Re-inspect; verify no dangling references remain.
|
|
457
|
+
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`.
|
|
258
458
|
|
|
259
459
|
**After every backtrack** append to `_brief/changelog.md`:
|
|
260
460
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# matrix-budget — matrix (pivot) view example
|
|
2
|
+
|
|
3
|
+
A minimal module whose only job is to demonstrate the `matrix` data view. The
|
|
4
|
+
simple-todo example has no cross-tab domain, so the matrix shape lives here.
|
|
5
|
+
|
|
6
|
+
## The shape
|
|
7
|
+
|
|
8
|
+
A matrix view is `rowAxis × colAxis` with a **cell** entity holding one record
|
|
9
|
+
per intersection. Here:
|
|
10
|
+
|
|
11
|
+
| Piece | Maps to |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `rowAxis` | `budget_category` records (a **dataset** axis), labelled by `name`, sorted by `code` |
|
|
14
|
+
| `colAxis` | the `quarter` dropdown options Q1–Q4 (a **dropdown** axis: `"budget_line.quarter"`) |
|
|
15
|
+
| `cell` | a `budget_line` record per (category, quarter); the editable value is `amount` |
|
|
16
|
+
|
|
17
|
+
So the grid reads: budget categories down the side, quarters across the top,
|
|
18
|
+
an editable amount in each cell.
|
|
19
|
+
|
|
20
|
+
## What it illustrates
|
|
21
|
+
|
|
22
|
+
- **Two axis kinds in one view** — a `dataset` row axis and a `dropdown` column
|
|
23
|
+
axis (the only worked example of the dropdown axis).
|
|
24
|
+
- **Editable cells** (`cell.editable: true`) — typing in an empty cell inserts a
|
|
25
|
+
`budget_line`; the matrix fills `rowKey` (`category_id`) and `colKey`
|
|
26
|
+
(`quarter`) automatically from the row/column it sits in.
|
|
27
|
+
- The **cell entity is the view's primary `dataSources` entity**, and
|
|
28
|
+
`cell.rowKey` / `cell.colKey` are real columns on it.
|
|
29
|
+
|
|
30
|
+
For a read-only `dataset × dataset` matrix (P&L: accounts × periods) plus the
|
|
31
|
+
`date` axis and the `lockedField` / `drill` / `seedFromRow` options, see the
|
|
32
|
+
§Matrix section of `dforge://reference/data-views`.
|
|
33
|
+
|
|
34
|
+
## Files
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
manifest.json
|
|
38
|
+
entities/budget_category.json row-axis (dataset) entity
|
|
39
|
+
entities/budget_line.json cell entity (FK+Reference + quarter dropdown + amount)
|
|
40
|
+
ui/data_views.json the matrix view (+ a categories grid)
|
|
41
|
+
security/roles.json S on the axis, SIUD on the editable cell
|
|
42
|
+
seed-data/01-categories.json three categories to populate the rows
|
|
43
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Budget categories — the row axis of the budget matrix",
|
|
3
|
+
"dbObject": "budget_category",
|
|
4
|
+
"toString": "{name}",
|
|
5
|
+
"traits": ["identity", "audit"],
|
|
6
|
+
"fields": {
|
|
7
|
+
"code": {
|
|
8
|
+
"dbDatatype": "varchar",
|
|
9
|
+
"fieldTypeCd": "text",
|
|
10
|
+
"flags": "VEM",
|
|
11
|
+
"maxLen": 20,
|
|
12
|
+
"orderNum": 10,
|
|
13
|
+
"description": "Code"
|
|
14
|
+
},
|
|
15
|
+
"name": {
|
|
16
|
+
"dbDatatype": "varchar",
|
|
17
|
+
"fieldTypeCd": "text",
|
|
18
|
+
"flags": "VEM",
|
|
19
|
+
"maxLen": 100,
|
|
20
|
+
"orderNum": 20,
|
|
21
|
+
"description": "Name"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|