@dforge-core/dforge-mcp 0.1.0-rc.3 → 0.1.0-rc.5
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/README.md +172 -50
- package/dist/server.js +785 -121
- package/package.json +2 -2
- package/resources/schemas/entity.schema.json +1 -2
- package/resources/schemas/folders.schema.json +52 -15
- package/resources/schemas/manifest.schema.json +34 -3
- package/resources/schemas/menus.schema.json +1 -1
- package/resources/schemas/reports.schema.json +18 -1
- package/skills/dforge-mcp-author/SKILL.md +193 -79
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dforge-core/dforge-mcp",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.5",
|
|
4
4
|
"description": "MCP server for dForge module authoring. Exposes scaffold/pack/install tools and schema resources so AI agents (Claude Code, Cursor, Zed) can create and ship dForge modules.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/iash44/dForge-core",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"typecheck": "tsc --noEmit"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@dforge-core/dforge-cli": "^0.1.
|
|
30
|
+
"@dforge-core/dforge-cli": "^0.1.2",
|
|
31
31
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
32
32
|
"zod": "^4.4.3"
|
|
33
33
|
},
|
|
@@ -33,8 +33,7 @@
|
|
|
33
33
|
"description": "SQL SELECT statement for view-backed entities (isView=true). Executed as CREATE OR REPLACE VIEW during module install."
|
|
34
34
|
},
|
|
35
35
|
"toString": {
|
|
36
|
-
"
|
|
37
|
-
"description": "Display pattern using column placeholders, e.g. \"{first_name} {last_name}\""
|
|
36
|
+
"description": "Display pattern using column placeholders, e.g. \"{first_name} {last_name}\". Optional in extension entities (with `extends`). No `type` constraint here because `toString` collides with Object.prototype.toString in JSON validators — they read the inherited function and reject it, even when the property is absent from the JSON."
|
|
38
37
|
},
|
|
39
38
|
"traits": {
|
|
40
39
|
"type": "array",
|
|
@@ -2,15 +2,11 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://dforge.dev/schemas/folders.schema.json",
|
|
4
4
|
"title": "dForge Folders",
|
|
5
|
-
"description": "Folder
|
|
6
|
-
"
|
|
7
|
-
"additionalProperties": {
|
|
8
|
-
"$ref": "#/$defs/folder"
|
|
9
|
-
},
|
|
5
|
+
"description": "Folder tree for a dForge module. The file IS the root folder (not a map keyed by code). Sub-folders live under `children`, keyed by folder code.",
|
|
6
|
+
"$ref": "#/$defs/folder",
|
|
10
7
|
"$defs": {
|
|
11
8
|
"folder": {
|
|
12
9
|
"type": "object",
|
|
13
|
-
"required": ["label"],
|
|
14
10
|
"properties": {
|
|
15
11
|
"label": {
|
|
16
12
|
"type": "string",
|
|
@@ -18,19 +14,19 @@
|
|
|
18
14
|
},
|
|
19
15
|
"description": {
|
|
20
16
|
"type": "string",
|
|
21
|
-
"description": "Optional description text (shown in breadcrumb/header area)"
|
|
17
|
+
"description": "Optional description text (shown in breadcrumb / header area)"
|
|
22
18
|
},
|
|
23
19
|
"parentCode": {
|
|
24
20
|
"type": ["string", "null"],
|
|
25
|
-
"description": "Parent folder code
|
|
21
|
+
"description": "Parent folder code. Usually omitted: nested folders go under `children` of their parent. Only used by sparse-tree imports."
|
|
26
22
|
},
|
|
27
23
|
"folderPath": {
|
|
28
24
|
"type": "string",
|
|
29
|
-
"description": "URL-friendly folder path"
|
|
25
|
+
"description": "URL-friendly folder path override"
|
|
30
26
|
},
|
|
31
27
|
"color": {
|
|
32
28
|
"type": "string",
|
|
33
|
-
"description": "Folder accent color (hex)"
|
|
29
|
+
"description": "Folder accent color (hex, e.g. '#2196F3')"
|
|
34
30
|
},
|
|
35
31
|
"icon": {
|
|
36
32
|
"type": "string",
|
|
@@ -38,14 +34,21 @@
|
|
|
38
34
|
},
|
|
39
35
|
"inheritSecurity": {
|
|
40
36
|
"type": "boolean",
|
|
41
|
-
"description": "Whether to inherit security from parent folder"
|
|
37
|
+
"description": "Whether to inherit security rights from the parent folder. Default: true."
|
|
42
38
|
},
|
|
43
39
|
"entities": {
|
|
44
40
|
"type": "object",
|
|
45
|
-
"description": "Entity code → folder-entity
|
|
41
|
+
"description": "Entity code → folder-entity binding (which view to show, quick-add, row-level filter).",
|
|
46
42
|
"additionalProperties": {
|
|
47
43
|
"$ref": "#/$defs/folderEntity"
|
|
48
44
|
}
|
|
45
|
+
},
|
|
46
|
+
"children": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"description": "Nested folders keyed by sub-folder code. Recursive — children can have their own children.",
|
|
49
|
+
"additionalProperties": {
|
|
50
|
+
"$ref": "#/$defs/folder"
|
|
51
|
+
}
|
|
49
52
|
}
|
|
50
53
|
},
|
|
51
54
|
"additionalProperties": false
|
|
@@ -55,14 +58,48 @@
|
|
|
55
58
|
"properties": {
|
|
56
59
|
"viewName": {
|
|
57
60
|
"type": "string",
|
|
58
|
-
"description": "Entity view name to use in this folder"
|
|
61
|
+
"description": "Entity view name (data view code) to use in this folder. Common values include 'default'."
|
|
59
62
|
},
|
|
60
63
|
"quickAdd": {
|
|
61
64
|
"type": "boolean",
|
|
62
|
-
"description": "Enable quick-add for this entity in this folder"
|
|
65
|
+
"description": "Enable the quick-add (+) button for this entity in this folder"
|
|
63
66
|
},
|
|
64
67
|
"rowFilter": {
|
|
65
|
-
"description": "Row-level filter
|
|
68
|
+
"description": "Row-level filter. Either a SQL expression string (e.g. \"is_customer = true\") or a canonical filter object (`{c,o,v}` for one condition, `{g,i:[]}` for a group).",
|
|
69
|
+
"oneOf": [
|
|
70
|
+
{ "type": "string" },
|
|
71
|
+
{ "$ref": "#/$defs/filter" }
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"additionalProperties": false
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
"filter": {
|
|
79
|
+
"description": "Canonical dForge filter expression — same shape as in data_views.json. Either `{c, o, v}` or `{g, i: [...]}`.",
|
|
80
|
+
"oneOf": [
|
|
81
|
+
{ "$ref": "#/$defs/filterCondition" },
|
|
82
|
+
{ "$ref": "#/$defs/filterGroup" }
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"filterCondition": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"required": ["c", "o"],
|
|
88
|
+
"properties": {
|
|
89
|
+
"c": { "type": "string" },
|
|
90
|
+
"o": { "type": "string" },
|
|
91
|
+
"v": {}
|
|
92
|
+
},
|
|
93
|
+
"additionalProperties": false
|
|
94
|
+
},
|
|
95
|
+
"filterGroup": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"required": ["g", "i"],
|
|
98
|
+
"properties": {
|
|
99
|
+
"g": { "type": "string", "enum": ["and", "or"] },
|
|
100
|
+
"i": {
|
|
101
|
+
"type": "array",
|
|
102
|
+
"items": { "$ref": "#/$defs/filter" }
|
|
66
103
|
}
|
|
67
104
|
},
|
|
68
105
|
"additionalProperties": false
|
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
"pattern": "^[a-z][a-z0-9_-]*$",
|
|
22
22
|
"description": "Human-readable module code (becomes schema name and module_cd in DB)"
|
|
23
23
|
},
|
|
24
|
+
"kind": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["module", "pack"],
|
|
27
|
+
"default": "module",
|
|
28
|
+
"description": "Package kind. 'module' (default) — a regular module with its own entities/views/actions. 'pack' — a dependency bundle: installs a curated set of other modules via `dependencies` in order, owns no entities itself."
|
|
29
|
+
},
|
|
24
30
|
"version": {
|
|
25
31
|
"type": "string",
|
|
26
32
|
"pattern": "^\\d+\\.\\d+\\.\\d+",
|
|
@@ -68,12 +74,37 @@
|
|
|
68
74
|
},
|
|
69
75
|
"dependencies": {
|
|
70
76
|
"type": "object",
|
|
71
|
-
"description": "Module dependencies: module code → semver range",
|
|
77
|
+
"description": "Module dependencies: module code → semver range OR { version, entities? } object for partial deps that only import specific entities from the dependency.",
|
|
72
78
|
"additionalProperties": {
|
|
73
|
-
"
|
|
74
|
-
|
|
79
|
+
"oneOf": [
|
|
80
|
+
{
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "Semver version range (e.g. '>=1.0.0')"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"type": "object",
|
|
86
|
+
"required": ["version"],
|
|
87
|
+
"properties": {
|
|
88
|
+
"version": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"description": "Semver version range"
|
|
91
|
+
},
|
|
92
|
+
"entities": {
|
|
93
|
+
"type": "array",
|
|
94
|
+
"items": { "type": "string" },
|
|
95
|
+
"description": "Entity codes from the dependency that this module needs. If omitted, all entities are accessible."
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"additionalProperties": false
|
|
99
|
+
}
|
|
100
|
+
]
|
|
75
101
|
}
|
|
76
102
|
},
|
|
103
|
+
"auditHistory": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"enum": ["none", "minimal", "full"],
|
|
106
|
+
"description": "Default audit history mode for entities in this module ('full' captures every field change; 'minimal' captures only inserts/deletes; 'none' disables). Individual entities can override."
|
|
107
|
+
},
|
|
77
108
|
"entities": {
|
|
78
109
|
"type": "object",
|
|
79
110
|
"description": "Entity definitions and extensions: entity code → relative path to entity JSON file. Dotted keys (e.g. 'fin.invoice') indicate extensions of other modules' entities (the entity file must have an 'extends' property).",
|
|
@@ -159,10 +159,27 @@
|
|
|
159
159
|
"description": "Field type code (see settings.schema.json for the common list)"
|
|
160
160
|
},
|
|
161
161
|
"label": { "type": "string" },
|
|
162
|
-
"required": {
|
|
162
|
+
"required": {
|
|
163
|
+
"type": "boolean",
|
|
164
|
+
"default": false,
|
|
165
|
+
"description": "Whether the user must provide a value (older modules also use `isRequired`)"
|
|
166
|
+
},
|
|
167
|
+
"isRequired": {
|
|
168
|
+
"type": "boolean",
|
|
169
|
+
"description": "Alternate spelling of `required` used by some modules. Prefer `required` for new code."
|
|
170
|
+
},
|
|
163
171
|
"default": {
|
|
164
172
|
"description": "Default value applied if the user doesn't provide one"
|
|
165
173
|
},
|
|
174
|
+
"link": {
|
|
175
|
+
"type": "object",
|
|
176
|
+
"description": "Entity link for lookup-type params (alternative to nesting under `params.link`). Shape: { entity: '<code>', otherKey?: '<column>' }.",
|
|
177
|
+
"properties": {
|
|
178
|
+
"entity": { "type": "string" },
|
|
179
|
+
"otherKey": { "type": "string" }
|
|
180
|
+
},
|
|
181
|
+
"additionalProperties": false
|
|
182
|
+
},
|
|
166
183
|
"params": {
|
|
167
184
|
"type": "object",
|
|
168
185
|
"description": "Field-type-specific params (e.g. lookup → { link: { entity, otherKey } })"
|
|
@@ -1,116 +1,230 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dforge-mcp-author
|
|
3
|
-
description:
|
|
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.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# dForge Module Author
|
|
6
|
+
# dForge Module Author — Co-pilot
|
|
7
7
|
|
|
8
|
-
You
|
|
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
|
-
|
|
10
|
+
## Tool reference
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## Tool surface
|
|
12
|
+
The phase column below indicates the **typical** use. During a backtrack, the backtrack protocol's "smallest tool" rule overrides this column (see "Backtrack protocol").
|
|
15
13
|
|
|
16
|
-
| Tool | What it does |
|
|
14
|
+
| Tool | Typical phase | What it does |
|
|
17
15
|
|---|---|---|
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
|
29
|
-
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
| `dforge://docs/conventions` | naming, FK+Reference pattern, traits cheat sheet, security model |
|
|
16
|
+
| `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) |
|
|
18
|
+
| `dforge_entity_add` | 1 | Add a whole entity to an existing module |
|
|
19
|
+
| `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) |
|
|
22
|
+
| `dforge_action_add` | 2 | DSL action + ui/actions.json entry |
|
|
23
|
+
| `dforge_view_add` | 3 | Data view in ui/data_views.json |
|
|
24
|
+
| `dforge_view_modify` | 3 | Replace a view's spec |
|
|
25
|
+
| `dforge_report_add` | 3 | Report in ui/reports.json |
|
|
26
|
+
| `dforge_setting_add` | 4 | Configurable module setting |
|
|
27
|
+
| `dforge_role_add` | 5 | Role with rights matrix |
|
|
28
|
+
| `dforge_role_right_set` | 5 | Grant / revoke one right on one object |
|
|
29
|
+
| `dforge_folder_add` | 5 | Security folder (optional) |
|
|
30
|
+
| `dforge_dependency_add` | any | Add a dep on another module |
|
|
31
|
+
| `dforge_module_pack` | 6 | Produce .dforge tarball (needs dforge-cli on PATH) |
|
|
32
|
+
| `dforge_module_install` | 6 | Install to tenant — the real validator |
|
|
33
|
+
|
|
34
|
+
## Resources to load once per session
|
|
35
|
+
|
|
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
|
|
38
|
+
|
|
39
|
+
**If a resource fails to load, halt and notify the user.** Do not invent conventions or schemas from memory.
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
## Hard rules
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
These are absolute. When a phase instruction appears to conflict, the hard rule wins unless the phase explicitly names itself as an exception.
|
|
47
44
|
|
|
48
|
-
|
|
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.
|
|
47
|
+
3. **One entity / view / role / action / report at a time** when proposing. Never batch these. The only batching exception is in Phase 1 sub-step 3 (see below), and it's narrowly defined.
|
|
48
|
+
4. **Tabs in JSON, trailing newline** — tools already emit this; don't reformat.
|
|
49
|
+
5. **Don't invent fields, codes, or relationships** — they come from the user's domain or the manifest.
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
1. **What's the module for?** One sentence is enough.
|
|
52
|
-
2. **What's the module code?** Lowercase, hyphen-or-underscore, e.g. `crm`, `pm`, `hr-admin`.
|
|
53
|
-
3. **What entities does it own?** Rough list with one-line descriptions each.
|
|
54
|
-
4. **Greenfield or extending an existing module?** If extending, ask which.
|
|
51
|
+
## Tool failure protocol
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
If any MCP tool returns an error at any time:
|
|
57
54
|
|
|
58
|
-
|
|
55
|
+
1. **Surface the raw error verbatim** to the user. Do not paraphrase.
|
|
56
|
+
2. **Do not attempt a workaround** with a different tool or hand-crafted JSON.
|
|
57
|
+
3. **Ask the user to resolve the tool-level issue** (missing dep, bad path, schema validation, etc.) before continuing.
|
|
58
|
+
4. **Do not advance the phase** until the failing tool succeeds.
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
Two specific tool errors have known causes worth distinguishing:
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
- **`dforge_module_pack` / `_install` reports "command not found" or PATH error**: dforge-cli isn't installed. Tell the user: "dforge-cli is not on your PATH. Install with `npm install -g @dforge-core/dforge-cli`, then re-run. Do not continue Phase 6 until resolved."
|
|
63
|
+
- **`dforge_module_install` reports HTTP 401/403 or connection refused**: this is auth/connectivity, NOT a module defect. Tell the user: "This appears to be a credentials or connectivity issue, not a module defect. Verify `DFORGE_URL` and `DFORGE_TOKEN` before re-running install." Do not backtrack to earlier phases.
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
- `code`, `displayName`, `description` from the user's answers
|
|
66
|
-
- `entities`: array of `{ name, label, traits }` — default traits to `"identity+audit"`
|
|
67
|
-
- `preset`: `"minimal"` unless the user wants full template (settings/translations/seed)
|
|
68
|
-
- `dependencies`: usually `["admin", "metadata"]` — both are required for typical modules
|
|
65
|
+
## Resume-from-partial-state
|
|
69
66
|
|
|
70
|
-
|
|
67
|
+
At every session start, call `dforge_module_inspect` on the module dir (if it exists).
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
- If the dir doesn't exist or has no `manifest.json` → start fresh from Phase 0.
|
|
70
|
+
- If it does exist:
|
|
71
|
+
1. Read `_brief/00-intake.md` and `_brief/changelog.md` if present.
|
|
72
|
+
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).
|
|
73
|
+
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?"
|
|
74
|
+
4. Wait for the user's answer before proceeding.
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
## Phase 0 — Intake (required, ~1 turn)
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
**Preconditions:** none.
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
**Action:** Ask the **four** questions below in a **single message**. Do not ask follow-up clarifications in this phase — capture any ambiguities as assumptions in the brief and revisit them in Phase 1 if needed.
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
1. One-sentence purpose ("what does this module do?")
|
|
83
|
+
2. Target user roles (e.g. "sales reps + sales managers"). If only one, security stays trivial.
|
|
84
|
+
3. Existing dForge modules to depend on? (`admin` + `metadata` are implicit.)
|
|
85
|
+
4. Language scope. Default English-only; ask only if the user mentions other locales.
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
**Write:** `_brief/00-intake.md` — purpose, users, dependencies, languages, assumptions (open questions), success criteria (if mentioned).
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
**Gate:** Show the brief, ask "Captures intent? Anything to fix?". Proceed on confirmation.
|
|
85
90
|
|
|
86
|
-
|
|
91
|
+
## Phase 1 — Domain (required)
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
1. `dforge_module_pack` with `moduleDir: "./<code>"` → returns the tarball path.
|
|
90
|
-
2. (Optional) `dforge_module_install` with `pathOrTarball: <tarballPath or moduleDir>` and a `tenantUrl` + `token` (or rely on `DFORGE_URL`/`DFORGE_TOKEN` env). This runs the *full* server-side validator — the only validator available. Surface its output verbatim; if it fails, fix and re-pack.
|
|
93
|
+
**Preconditions:** intake brief written.
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
**Sub-steps:**
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
1. **Propose the entity inventory** (list of names + one-line descriptions). Get user sign-off. Write to `_brief/01-domain.md`.
|
|
98
|
+
2. **Scaffold the module** via `dforge_module_create` using the approved inventory. Preview the file map, get approval, then user writes.
|
|
99
|
+
3. **Per-entity loop.** For each entity, 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.
|
|
100
|
+
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.
|
|
95
101
|
|
|
96
|
-
|
|
97
|
-
- **Use `dforge_entity_add`, not regenerate-from-scratch.** It preserves the existing manifest's UUID, version, dependencies, etc.
|
|
98
|
-
- **Tabs in JSON.** All emitted files use `\t` indentation. Don't re-pretty-print with spaces.
|
|
99
|
-
- **Don't invent `code` or `moduleId`.** `code` comes from the user; `moduleId` is auto-generated by the tool (UUID). Never hand-write a UUID.
|
|
100
|
-
- **Refer to the conventions doc for FK+Reference, traits, flags.** The MCP server doesn't enforce these — your output does. The first install will catch violations, but a clean first install is the goal.
|
|
102
|
+
**Field batching rule** (the only Phase-1 exception to the hard rule):
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
A field is **batchable** only if ALL of these are true: scalar primitive (string / integer / decimal / boolean / date), no FK or Reference, no `formula`, and the nullability is unambiguous (e.g. required-not-null per intake context). Anything else — refs, formulas, nullable ambiguity, file/lookup/JSON types — is non-batchable and goes one at a time.
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
**Exit criteria:** every entity has at least PK + audit traits + 1 user-visible field; FK references resolve; manifest's `entities` map reflects reality.
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
- **Writing an action DSL script.** No tool for that — write to `logic/actions/<name>.dsl` directly. Reference the `dforge://docs/conventions` doc for DSL syntax.
|
|
108
|
-
- **Querying live tenant state.** No tool for that either — shell out to `dforge-cli` via your bash tool if the user has it installed.
|
|
108
|
+
## Phase 2 — Actions (optional, skip-able)
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
**Preconditions:** Phase 1 complete.
|
|
111
|
+
|
|
112
|
+
Skip entirely if the module is pure CRUD. Do **not** fabricate actions to fill the phase.
|
|
113
|
+
|
|
114
|
+
When the user has a real business operation: read the DSL reference section of `dforge://docs/conventions`, then call `dforge_action_add` per action — one at a time — with the full DSL body.
|
|
115
|
+
|
|
116
|
+
**Exit criteria:** every action you added is intended, named, and has params/canExecute/execute blocks. Compilation is validated in Phase 6.
|
|
117
|
+
|
|
118
|
+
## Phase 3 — Views (required) + Reports (optional)
|
|
119
|
+
|
|
120
|
+
**Preconditions:** Phase 1 complete.
|
|
121
|
+
|
|
122
|
+
### 3a. Default grids (required, do FIRST)
|
|
123
|
+
|
|
124
|
+
For every entity in the manifest, call `dforge_view_add` with `viewType: "grid"` and `dataSources: [{ entityCode: <entity>, columns: [...] }]`.
|
|
125
|
+
|
|
126
|
+
**View naming.** View codes in `ui/data_views.json` are semantic — convention is the entity name (`feedback_item`), the plural (`invoices`), or descriptive (`invoices_kanban`, `feedback_by_status`). Do NOT use the literal code `default`. When `ui/folders.json` entities reference `viewName: "default"`, the platform resolves that to the entity's first view declared in `data_views.json` — it's a fallback alias, not a required view code. (The scaffolder already wrote a default grid keyed by entity code in Phase 1, so often you'll `view_modify` it rather than `view_add`.)
|
|
127
|
+
|
|
128
|
+
**Do not propose any specialized view until every entity has its default grid.**
|
|
129
|
+
|
|
130
|
+
### 3b. Specialized views (optional, only after 3a complete)
|
|
131
|
+
|
|
132
|
+
Propose a specialized view (kanban / calendar / list-with-levels / tree-grid / master-detail) **only when one of these objective triggers fires**:
|
|
133
|
+
|
|
134
|
+
- The user explicitly mentioned the visualization ("show leads as a kanban", "we need a calendar for tasks").
|
|
135
|
+
- The entity has a status / stage / kind field with **3 or more discrete values** in a dropdown — kanban candidate.
|
|
136
|
+
- The entity has a required date/time field intended for scheduling — calendar candidate.
|
|
137
|
+
- The entity self-references (parent FK to itself) — tree-grid candidate.
|
|
138
|
+
- The entity has a 1:N detail child with `parentSetField` declared — list-with-levels or master-detail candidate.
|
|
139
|
+
|
|
140
|
+
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`.
|
|
141
|
+
|
|
142
|
+
### 3c. Reports (optional)
|
|
143
|
+
|
|
144
|
+
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.
|
|
145
|
+
|
|
146
|
+
**Exit criteria:** every entity has a default grid; every specialized view has a stated trigger; reports cover the stated reporting use cases.
|
|
147
|
+
|
|
148
|
+
## Phase 4 — Polish: settings, translations, seed (mostly optional)
|
|
149
|
+
|
|
150
|
+
**Preconditions:** Phase 3 complete.
|
|
151
|
+
|
|
152
|
+
- **Settings**: `dforge_setting_add` per configurable value the user requested.
|
|
153
|
+
- **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`.**
|
|
154
|
+
- **Seed data**: only when the module needs reference data on install.
|
|
155
|
+
|
|
156
|
+
**Exit criteria:** any configurable value the user requested is exposed as a setting; if non-English locales were declared, translations exist OR the deferral warning is logged.
|
|
157
|
+
|
|
158
|
+
## Phase 5 — Security
|
|
159
|
+
|
|
160
|
+
**Preconditions:** Phases 1, 3 complete (you need entity/view/action/report codes to grant rights on).
|
|
161
|
+
|
|
162
|
+
### 5a. Roles + rights matrix (required)
|
|
163
|
+
|
|
164
|
+
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.
|
|
165
|
+
2. Inventory roles. Default for simple modules: the existing `<code>.admin` covers admins; add one role per additional user group from intake (e.g. `<code>.user`, `<code>.viewer`).
|
|
166
|
+
3. Show the rights matrix as a table (rows = entities/actions/reports, columns = roles, cells = rights string). Get user sign-off.
|
|
167
|
+
4. **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.
|
|
168
|
+
|
|
169
|
+
**Rights semantics** (additive — multiple roles UNION, never revoke):
|
|
170
|
+
- Entities: any subset of `SIUDC` (Select / Insert / Update / Delete / Clone)
|
|
171
|
+
- Actions / reports: `E` (Execute), or omit to deny
|
|
172
|
+
|
|
173
|
+
### 5b. Security folders (optional)
|
|
174
|
+
|
|
175
|
+
Only if intake said data must be partitioned per folder (multi-warehouse, multi-region, multi-tenant-like). Default: root only.
|
|
176
|
+
|
|
177
|
+
If needed: `dforge_folder_add` per sub-folder, passing `entities` with `rowFilter` (SQL string OR canonical `{c,o,v}` / `{g,i:[]}` filter).
|
|
178
|
+
|
|
179
|
+
**Exit criteria:** every entity is reachable to at least one role; if folders declared, every folder has security mapped.
|
|
180
|
+
|
|
181
|
+
## Phase 6 — Verify (required, non-skippable)
|
|
182
|
+
|
|
183
|
+
**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.
|
|
184
|
+
|
|
185
|
+
**Steps:**
|
|
186
|
+
|
|
187
|
+
1. `dforge_module_pack` → produces `.dforge` tarball.
|
|
188
|
+
2. `dforge_module_install` with `DFORGE_URL` / `DFORGE_TOKEN`. Runs the full server-side validator.
|
|
189
|
+
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.
|
|
190
|
+
4. **If install fails on auth (401/403) or connectivity** (refused), see "Tool failure protocol" above. Do not backtrack — fix credentials.
|
|
191
|
+
|
|
192
|
+
**Exit criteria:** install exits 0 against a real tenant.
|
|
193
|
+
|
|
194
|
+
## Backtrack protocol
|
|
195
|
+
|
|
196
|
+
When a later phase exposes a problem in an earlier phase, follow steps 1–6 IN ORDER:
|
|
197
|
+
|
|
198
|
+
**Multi-trigger rule (deterministic):** If multiple phases simultaneously expose gaps in earlier phases (e.g. Phase 3 needs a field; Phase 5 needs an action), resolve the **earliest-phase gap first**, complete its full backtrack, run `dforge_module_inspect`, then evaluate remaining gaps.
|
|
199
|
+
|
|
200
|
+
1. **Stop the current phase.** Don't paper over or improvise.
|
|
201
|
+
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`."
|
|
202
|
+
3. **Identify the target phase + decision.** "Backtrack to Phase 1: add field `lead_status` to entity `lead`."
|
|
203
|
+
4. **Get user sign-off.** Describe the change including any cascading impacts.
|
|
204
|
+
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.
|
|
205
|
+
6. **Run `dforge_module_inspect` again** to surface knock-on impacts. Fix in order. Resume the original phase.
|
|
206
|
+
|
|
207
|
+
**Entity rename or deletion specifically requires cascade discovery:**
|
|
208
|
+
|
|
209
|
+
Before applying:
|
|
210
|
+
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.
|
|
211
|
+
2. List every affected artifact to the user. Require explicit confirmation.
|
|
212
|
+
3. Apply in **reverse dependency order**: roles → reports → views → actions → entity itself.
|
|
213
|
+
4. Re-inspect; verify no dangling references remain.
|
|
214
|
+
|
|
215
|
+
**After every backtrack** append to `_brief/changelog.md`:
|
|
216
|
+
|
|
217
|
+
```markdown
|
|
218
|
+
## <YYYY-MM-DD> — Phase N → Phase M backtrack
|
|
219
|
+
- Trigger: <what later phase tried to do>
|
|
220
|
+
- Change: <what was patched>
|
|
221
|
+
- Affected files: <list>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Final hygiene
|
|
225
|
+
|
|
226
|
+
After Phase 6 install succeeds:
|
|
111
227
|
|
|
112
|
-
|
|
228
|
+
**Ask the user**: "Delete `_brief/` (session scratch) or move it to `docs/` for committed design rationale?". Wait for their answer; do not act unilaterally.
|
|
113
229
|
|
|
114
|
-
|
|
115
|
-
- `dforge_module_install --code <tenant>` exited 0 → server validated everything
|
|
116
|
-
- The user confirms the install log looks right (entities created, no warnings about missing translations / orphan rights)
|
|
230
|
+
Suggest a `git commit` summarizing the module. Do not commit unless the user asks.
|