@loj-lang/cli 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +87 -0
  2. package/agent-assets/loj-authoring/SKILL.md +179 -0
  3. package/agent-assets/loj-authoring/agents/openai.yaml +3 -0
  4. package/agent-assets/loj-authoring/metadata.json +6 -0
  5. package/agent-assets/loj-authoring/references/backend-family.md +340 -0
  6. package/agent-assets/loj-authoring/references/backend-targets.md +171 -0
  7. package/agent-assets/loj-authoring/references/frontend-family.md +794 -0
  8. package/agent-assets/loj-authoring/references/frontend-runtime-trace.md +204 -0
  9. package/agent-assets/loj-authoring/references/policy-rules-proof.md +178 -0
  10. package/agent-assets/loj-authoring/references/project-and-transport.md +454 -0
  11. package/agent-assets/loj-authoring/references/workflow-flow-proof.md +263 -0
  12. package/dist/database-native-sql.d.ts +4 -0
  13. package/dist/database-native-sql.d.ts.map +1 -0
  14. package/dist/database-native-sql.js +266 -0
  15. package/dist/database-native-sql.js.map +1 -0
  16. package/dist/env.d.ts +31 -0
  17. package/dist/env.d.ts.map +1 -0
  18. package/dist/env.js +229 -0
  19. package/dist/env.js.map +1 -0
  20. package/dist/fastapi-dev-runner.d.ts +3 -0
  21. package/dist/fastapi-dev-runner.d.ts.map +1 -0
  22. package/dist/fastapi-dev-runner.js +263 -0
  23. package/dist/fastapi-dev-runner.js.map +1 -0
  24. package/dist/flow-proof.d.ts +3 -0
  25. package/dist/flow-proof.d.ts.map +1 -0
  26. package/dist/flow-proof.js +2 -0
  27. package/dist/flow-proof.js.map +1 -0
  28. package/dist/index.d.ts +36 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +5353 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/rules-proof.d.ts +3 -0
  33. package/dist/rules-proof.d.ts.map +1 -0
  34. package/dist/rules-proof.js +2 -0
  35. package/dist/rules-proof.js.map +1 -0
  36. package/package.json +49 -0
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @loj-lang/cli
2
+
3
+ Repo-level orchestration CLI for `loj.project.yaml`.
4
+
5
+ Current implemented commands:
6
+
7
+ - `loj validate`
8
+ - `loj build`
9
+ - `loj dev`
10
+ - `loj status`
11
+ - `loj stop`
12
+ - `loj doctor`
13
+ - `loj rules validate`
14
+ - `loj rules build`
15
+ - `loj flow validate`
16
+ - `loj flow build`
17
+ - `loj agent install <codex|windsurf|generic>`
18
+ - `loj agent add <codex|windsurf|generic> --from <source>`
19
+ - `loj agent export <codex|windsurf|generic> --out-dir <dir>`
20
+
21
+ This package coordinates sibling frontend and backend targets rather than replacing target-local CLIs.
22
+ Its agent commands only distribute the bundled `loj-authoring` skill; they do not replace the
23
+ canonical syntax docs or the manifest/inspect/trace contract.
24
+
25
+ Recommended project-shell loop:
26
+
27
+ ```bash
28
+ # inspect dependencies, generated outputs, linked files, and current dev state
29
+ npx @loj-lang/cli doctor ./loj.project.yaml
30
+
31
+ # start the managed host/backend loop
32
+ npx @loj-lang/cli dev ./loj.project.yaml
33
+
34
+ # inspect current URLs, services, probes, and debugger endpoints
35
+ npx @loj-lang/cli status ./loj.project.yaml
36
+
37
+ # stop the active managed session
38
+ npx @loj-lang/cli stop ./loj.project.yaml
39
+ ```
40
+
41
+ Examples:
42
+
43
+ ```bash
44
+ # validate a standalone policy/rules proof file
45
+ npx @loj-lang/cli rules validate ./policies/invoice-access.rules.loj
46
+
47
+ # emit a target-neutral semantic manifest
48
+ npx @loj-lang/cli rules build ./policies/invoice-access.rules.loj --out-dir ./generated/rules
49
+
50
+ # validate a standalone workflow/state-machine proof file
51
+ npx @loj-lang/cli flow validate ./workflows/booking-process.flow.loj
52
+
53
+ # emit a shared workflow manifest
54
+ npx @loj-lang/cli flow build ./workflows/booking-process.flow.loj --out-dir ./generated/flow
55
+
56
+ # install into CODEX_HOME/skills or ~/.codex/skills
57
+ npx @loj-lang/cli agent install codex
58
+
59
+ # install into WINDSURF_HOME/skills or ~/.codeium/windsurf/skills
60
+ npx @loj-lang/cli agent install windsurf
61
+
62
+ # vendor a pinned project copy under ./.loj/agents/codex/skills
63
+ npx @loj-lang/cli agent install codex --scope project
64
+
65
+ # add a skill bundle from a local or remote source
66
+ npx @loj-lang/cli agent add codex --from ./tooling/skills/loj-authoring
67
+
68
+ # install into any explicit skills directory
69
+ npx @loj-lang/cli agent install generic --skills-dir ~/.my-agent/skills
70
+
71
+ # export into any custom directory
72
+ npx @loj-lang/cli agent export codex --out-dir ./tooling/skills
73
+ ```
74
+
75
+ The current `.rules.loj` slice is intentionally narrow: one named rule set per file, grouped
76
+ `allow/deny`, `eligibility`, `validate`, and `derive` entries, standalone manifest emission
77
+ through `loj rules build`, plus narrow backend-family linkage through `resource auth.policy`,
78
+ `resource create.rules`, and `readModel rules`. It is still not orchestrated through
79
+ `loj.project.yaml`. The current `.flow.loj` slice is even narrower: standalone validate/build
80
+ only, with one `workflow <name>:` block compiling to a shared workflow manifest.
81
+
82
+ Local workspace commands:
83
+
84
+ ```bash
85
+ npm run build --workspace=@loj-lang/cli
86
+ npm run test --workspace=@loj-lang/cli
87
+ ```
@@ -0,0 +1,179 @@
1
+ ---
2
+ name: loj-authoring
3
+ description: >
4
+ Use this skill when creating, editing, reviewing, or debugging `.web.loj` / legacy `.rdsl`
5
+ files, `.api.loj` / legacy `.sdsl` files, `.style.loj` files, linked `.rules.loj` / `.flow.loj`
6
+ slices, or `loj.project.yaml` orchestration files for the Loj DSL framework. Covers current
7
+ web/api/style family syntax, linked rules/workflow/SQL escape hatches, project-shell
8
+ database/runtime/dev commands, and how those inputs map to generated React, Spring Boot, and
9
+ FastAPI output.
10
+ ---
11
+
12
+ # Loj Authoring
13
+
14
+ Use this skill for:
15
+
16
+ - frontend-family `.web.loj` / legacy `.rdsl`
17
+ - style `.style.loj`
18
+ - backend-family `.api.loj` / legacy `.sdsl`
19
+ - linked `.rules.loj`
20
+ - linked `.flow.loj`
21
+ - `loj.project.yaml`
22
+
23
+ (For codex) If this skill is missing outside the repo, current install/export commands are:
24
+
25
+ - `npx @loj-lang/cli agent install codex`
26
+ - `npx @loj-lang/cli agent install codex --scope project`
27
+ - `npx @loj-lang/cli agent export codex --out-dir ./tooling/skills`
28
+
29
+ All portable technique references required by this skill live in `references/`.
30
+ You may not have knowledge of the Loj DSL framework at this time, so you must read the references to understand the syntax and semantics of the DSL.
31
+ This skill should remain usable even when vendored outside the repo.
32
+
33
+ | Task involves | Read |
34
+ |---|---|
35
+ | frontend-family syntax, models/resources/pages/read-model pages, style/SEO/assets, workflow-linked surfaces | `references/frontend-family.md` |
36
+ | generated React runtime assumptions, runtime imports, inspect/trace behavior | `references/frontend-runtime-trace.md` |
37
+ | backend-family syntax, models/resources/read-models, linked rules/workflow/SQL, transaction defaults | `references/backend-family.md` |
38
+ | Spring Boot / FastAPI target-specific output and escape-hatch constraints | `references/backend-targets.md` |
39
+ | standalone policy/rules proof syntax and current linkage boundaries | `references/policy-rules-proof.md` |
40
+ | standalone workflow/state-machine syntax and current linked resource behavior | `references/workflow-flow-proof.md` |
41
+ | `loj.project.yaml`, project-shell database/runtime slices, dev/status/doctor flows, frontend↔backend transport alignment | `references/project-and-transport.md` |
42
+
43
+ ## Workflow
44
+
45
+ 1. Identify whether the task is frontend-family, backend-family, linked rules/workflow, or project-shell orchestration.
46
+ 2. For new source, prefer canonical suffixes and target names:
47
+ - `.web.loj`, `.api.loj`
48
+ - `type: web`, `type: api`
49
+ 3. Read only the minimal bundled reference slice from the router above.
50
+ 4. Generate only implemented syntax from those references.
51
+ 5. If the requested shape is outside the current slice, say so and use the narrowest implemented escape hatch or project/runtime profile.
52
+
53
+ ## Command Shortcuts
54
+
55
+ Default recommendation: use project-shell commands first whenever a `loj.project.yaml` exists.
56
+
57
+ - preferred default:
58
+ - `loj validate loj.project.yaml`
59
+ - `loj build loj.project.yaml`
60
+ - `loj dev loj.project.yaml`
61
+ - `loj rebuild loj.project.yaml --target <alias>`
62
+ - `loj restart loj.project.yaml --service <host|server|all>`
63
+ - `loj status loj.project.yaml`
64
+ - `loj stop loj.project.yaml`
65
+ - `loj doctor loj.project.yaml`
66
+ - single-target project-shell:
67
+ - `loj validate loj.project.yaml --target <alias>`
68
+ - `loj build loj.project.yaml --target <alias>`
69
+ - `loj dev loj.project.yaml --target <alias>`
70
+ - `loj status loj.project.yaml --target <alias>`
71
+ - `loj doctor loj.project.yaml --target <alias>`
72
+ - standalone rules proof:
73
+ - `loj rules validate <entry.rules.loj>`
74
+ - `loj rules build <entry.rules.loj> --out-dir <dir>`
75
+ - standalone workflow proof:
76
+ - `loj flow validate <entry.flow.loj>`
77
+ - `loj flow build <entry.flow.loj> --out-dir <dir>`
78
+ - family-local fallback:
79
+ - `rdsl validate <entry.web.loj>`
80
+ - `rdsl build <entry.web.loj> --out-dir <dir>`
81
+ - `sdsl validate <entry.api.loj>`
82
+ - `sdsl build <entry.api.loj> --out-dir <dir>`
83
+
84
+ Use `rdsl` / `sdsl` mainly for pure single-family work or compiler/debug tasks. Do not treat them as
85
+ the default entrypoint for new multi-file app work when `loj.project.yaml` exists.
86
+
87
+ Useful debugging commands:
88
+
89
+ - `rdsl inspect <entry.web.loj|build-dir> [--node <id>]`
90
+ - `rdsl trace <entry.web.loj|build-dir> <generated-file:line[:col]>`
91
+
92
+ ## Hard Rules
93
+
94
+ ### Common
95
+
96
+ - Files are a strict YAML subset: no anchors, aliases, merge keys, or custom tags.
97
+ - For new files, use canonical suffixes; keep `.rdsl` / `.sdsl` only for legacy edits.
98
+ - Prefer single-file apps for small demos. Use `imports:` only when the app really benefits from splitting by domain.
99
+ - `imports:` entries are relative family-source paths (`.web.loj` / `.rdsl`, `.api.loj` / `.sdsl`) or directories ending with `/`.
100
+ - Nested imports are allowed. Cycles are invalid. Directory imports expand direct children only.
101
+ - One root file owns `app:` and `compiler:`. Module files may not contain them.
102
+ - Model/resource names must stay unique across the merged namespace.
103
+ - Keep primitives target-neutral and business-oriented.
104
+ - Do not leak framework/runtime specifics into the DSL source.
105
+
106
+ ### Frontend-family
107
+
108
+ - Expression language stays constrained. No loops, statements, closures, or inline JS outside implemented escape hatches.
109
+ - Escape preference: built-in DSL > `@expr` > `@fn` > `@custom`.
110
+ - Treat suffix-bearing escape paths such as `.ts` / `.tsx` as deliberate frontend lock-in, not the default style.
111
+ - `toast` accepts static string or descriptor only.
112
+ - Current UI-copy descriptor support is still narrow:
113
+ - use plain strings for fixed copy
114
+ - use `{ key?, defaultMessage?, values? }` when future i18n or scalar-literal interpolation matters
115
+ - copy descriptors are currently implemented only on the documented title/label/date-navigation/SEO slices
116
+ - `app.style` links `.style.loj`; shell-level `style:` references are currently narrow and do not imply table internals or responsive/mobile variants.
117
+ - `app.seo`, `page.seo`, and `@asset(...)` are web metadata/asset surfaces, not style DSL features.
118
+ - Escape file paths resolve relative to the declaring `.web.loj` file, not the root file.
119
+
120
+ ### Backend-family
121
+
122
+ - `compiler:` must use an implemented `target + language + profile` triple.
123
+ - `auth:` describes policy intent, not framework internals.
124
+ - `operations:` controls generated CRUD endpoints; all default `true` if omitted.
125
+ - No frontend concepts like pages, navigation, columns, or toast in `.api.loj`.
126
+ - Do not encode Java/Python/SQLAlchemy/JPA into DSL primitives; keep them in target/profile or escape-hatch code.
127
+ - `resource auth.policy` may use either `@fn("./policies/x")` or `@rules("./policies/x")`.
128
+ - `resource create.rules` may use `@rules("./rules/x")` for narrow create eligibility/validation.
129
+ - `readModel rules` may use `@rules("./rules/x")` for narrow eligibility/validation/derivation.
130
+ - `resource workflow` may use `@flow("./workflows/x")`.
131
+ - `readModel handler` may use `@fn("./handlers/x")` or narrow file-backed `@sql("./queries/x")`.
132
+ - `@sql(...)` is read-model-only, read-only, and file-backed; keep procedures and write-oriented SQL in `@fn(...)`.
133
+ - Generated write paths are transactional by default in the implemented targets; do not invent `transactional: true`.
134
+
135
+ ### `.rules.loj`
136
+
137
+ - Use exactly one top-level `rules <name>:` block per file.
138
+ - Stay within the current slice:
139
+ - `allow/deny <operation>`
140
+ - `eligibility <name>`
141
+ - `validate <name>`
142
+ - `derive <field>`
143
+ - shared `when`, optional `or`, optional `message`, and list-only `scopeWhen/scope` on auth entries
144
+ - `.rules.loj` is linked only through the documented frontend/backend slices; do not invent new integration points.
145
+ - Do not put `.rules.loj` under `loj.project.yaml`.
146
+
147
+ ### `.flow.loj`
148
+
149
+ - Use exactly one top-level `workflow <name>:` block per file.
150
+ - Stay within the current slice:
151
+ - `model`
152
+ - `field`
153
+ - `states`
154
+ - optional `wizard.steps`
155
+ - `transitions`
156
+ - `.flow.loj` is linked only through `resource workflow: ...` in `.web.loj` / `.api.loj`.
157
+ - Do not invent project-shell workflow targets, page-level router DSL, or framework-specific state-machine syntax.
158
+
159
+ ### `loj.project.yaml`
160
+
161
+ - `loj.project.yaml` is orchestration only. Never place `model`, `resource`, `page`, or backend implementation details in it.
162
+ - Prefer `type: web` / `type: api`. Treat `type: rdsl` / `type: sdsl` as deprecated legacy aliases.
163
+ - `entry` should point at canonical suffixes for new examples: `app.web.loj`, `app.api.loj`.
164
+ - Database/runtime/dev orchestration belongs here, not in `.web.loj` / `.api.loj`.
165
+ - Keep `dev:` and `targets.<alias>.runtime` narrow and declarative. Do not turn them into arbitrary scripting or general ops config.
166
+ - For new projects, default to the recommended bundled directory structure from `references/project-and-transport.md` instead of inventing an ad hoc tree.
167
+
168
+ ## Review Before Final Output
169
+
170
+ - Valid strict-YAML-subset?
171
+ - Canonical suffix/type chosen for new files unless editing legacy input?
172
+ - Read from bundled `references/` rather than guessing from stale memory?
173
+ - Top-level keys allowed for this file type (root vs module)?
174
+ - Model/resource names unique project-wide?
175
+ - Rules/effects within the implemented slice?
176
+ - Escape hatches narrow and intentional?
177
+ - No framework/runtime detail leaking into family DSL source?
178
+ - For `.api.loj`: implemented `target/language/profile` triple and current linkage boundaries respected?
179
+ - For `loj.project.yaml`: orchestration-only, with `web/api` preferred and database/runtime/dev slices kept narrow?
@@ -0,0 +1,3 @@
1
+ interface:
2
+ display_name: "Loj Authoring"
3
+ short_description: "Author `.web.loj`, `.api.loj`, and `loj.project.yaml` using canonical Loj patterns"
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": "0.5.0",
3
+ "organization": "Loj",
4
+ "date": "March 2026",
5
+ "abstract": "Unified AI authoring skill for the Loj DSL framework. Covers frontend-family .web.loj (legacy .rdsl), backend-family .api.loj (legacy .sdsl), style .style.loj, linked .rules.loj and .flow.loj sub-DSL slices, and loj.project.yaml orchestration authoring, review, and debugging. Uses bundled references for current syntax, runtime, transport, project-shell, style, and generated-output guidance."
6
+ }
@@ -0,0 +1,340 @@
1
+ # Backend-Family Authoring (`.api.loj`, legacy `.sdsl`)
2
+
3
+ Use this reference when authoring or reviewing backend-family source files.
4
+
5
+ ## Defaults
6
+
7
+ - Prefer `.api.loj` for new files. Use `.sdsl` only for legacy edits.
8
+ - Files are a strict YAML subset:
9
+ - no anchors
10
+ - no aliases
11
+ - no merge keys
12
+ - no custom tags
13
+
14
+ Implemented compiler triples today:
15
+
16
+ - `spring-boot / java / mvc-jpa-security`
17
+ - `fastapi / python / rest-sqlalchemy-auth`
18
+
19
+ If `compiler:` is omitted, default is the Spring triple above.
20
+
21
+ ## File Shape
22
+
23
+ Root files may contain:
24
+
25
+ - `app:`
26
+ - optional `compiler:`
27
+ - optional `imports:`
28
+ - `model <Name>:`
29
+ - `resource <name>:`
30
+ - `readModel <name>:`
31
+
32
+ Module files may contain:
33
+
34
+ - optional `imports:`
35
+ - `model <Name>:`
36
+ - `resource <name>:`
37
+ - `readModel <name>:`
38
+
39
+ Module files may not contain `app:` or `compiler:`.
40
+
41
+ ## Imports
42
+
43
+ - Use single-file for small demos.
44
+ - Split by domain only when the service grows.
45
+ - `imports:` entries must be relative `.api.loj` / `.sdsl` paths or directories ending in `/`.
46
+ - Nested imports are allowed.
47
+ - Import cycles are invalid.
48
+ - Directory imports expand direct child family files only, sorted lexicographically.
49
+
50
+ ## `app:`
51
+
52
+ ```yaml
53
+ app:
54
+ name: "Booking Service"
55
+ package: "com.example.booking"
56
+ ```
57
+
58
+ Rules:
59
+
60
+ - `name` is required
61
+ - `package` is required
62
+ - `package` must be a valid dotted Java-style package root
63
+ - do not place auth provider, database vendor, shutdown, proxy, or deploy details here
64
+
65
+ ## `compiler:`
66
+
67
+ ```yaml
68
+ compiler:
69
+ target: spring-boot
70
+ language: java
71
+ profile: mvc-jpa-security
72
+ ```
73
+
74
+ Rules:
75
+
76
+ - `target`, `language`, and `profile` must form an implemented triple
77
+ - do not encode language into `target`
78
+ - `compiler:` is for generation target selection, not business semantics
79
+
80
+ ## `model <Name>:`
81
+
82
+ Example:
83
+
84
+ ```yaml
85
+ model Booking:
86
+ reference: string @required @unique
87
+ baseFare: decimal @required
88
+ travelDate: date @required
89
+ status: enum(DRAFT, READY, CONFIRMED, FAILED)
90
+ memberId: belongsTo(Member)
91
+ passengers: hasMany(Passenger, by: bookingId)
92
+ createdAt: datetime @createdAt
93
+ updatedAt: datetime @updatedAt
94
+ ```
95
+
96
+ Types:
97
+
98
+ - `string`
99
+ - `text`
100
+ - `integer`
101
+ - `long`
102
+ - `decimal`
103
+ - `boolean`
104
+ - `datetime`
105
+ - `date`
106
+ - `enum(A, B, C)`
107
+ - `belongsTo(Model)`
108
+ - `hasMany(Model, by: field)`
109
+
110
+ Decorators:
111
+
112
+ - `@required`
113
+ - `@email`
114
+ - `@unique`
115
+ - `@minLen(n)`
116
+ - `@maxLen(n)`
117
+ - `@createdAt`
118
+ - `@updatedAt`
119
+
120
+ Implicit persistence identity:
121
+
122
+ ```yaml
123
+ id: long
124
+ ```
125
+
126
+ Do not declare `id` manually in `v0.1`.
127
+
128
+ Relation rules:
129
+
130
+ - `belongsTo(Model)` is supported for narrow foreign-key-style relations
131
+ - `hasMany(Model, by: field)` is inverse metadata only
132
+ - `hasMany(..., by: ...)` must point to a target-model field declared as
133
+ `belongsTo(CurrentModel)`
134
+ - current backend-family relation support is metadata plus generated foreign-key handling; it is not
135
+ a query DSL
136
+
137
+ ## `readModel <name>:`
138
+
139
+ Example:
140
+
141
+ ```yaml
142
+ readModel flightAvailability:
143
+ api: /api/flight-availability
144
+ auth:
145
+ mode: public
146
+ inputs:
147
+ departureAirport: string @required
148
+ departureDate: date @required
149
+ result:
150
+ flightNo: string
151
+ quotedFare: decimal
152
+ handler: '@sql("./queries/flightAvailability")'
153
+ rules: '@rules("./rules/flight-availability")'
154
+ ```
155
+
156
+ Rules:
157
+
158
+ - current read-models are narrow named GET list surfaces, not a generic query DSL
159
+ - `api` is required and must start with `/`
160
+ - `inputs:` and `result:` reuse model-style field authoring, but currently support scalar and enum
161
+ field shapes only
162
+ - current `inputs:` support only `@required`
163
+ - current `inputs:` do not support `datetime`
164
+ - current `result:` fields do not support decorators
165
+ - `auth:` currently supports only `mode` / `roles`
166
+ - `handler:` is required and must use `@fn("./path")` or `@sql("./path")`
167
+ - extensionless logical ids are preferred; target resolution uses `.java` for Spring and `.py` for
168
+ FastAPI
169
+ - `@sql(...)` is read-model-only and intentionally narrow:
170
+ - file-backed query only
171
+ - read-only `SELECT` / `WITH`
172
+ - no procedures, `CALL`, `EXEC`, or write SQL
173
+ - alias columns to declared `result:` field names
174
+ - `rules:` is optional and must use `@rules("./rules/x")`
175
+ - current read-model rules support only:
176
+ - `eligibility`
177
+ - `validate`
178
+ - `derive`
179
+ - `allow/deny` entries are rejected on backend read-model linkage
180
+ - read-model rule expressions currently allow:
181
+ - `currentUser.*`
182
+ - `input.<field>`
183
+ - `item.<resultField>` inside derivations
184
+ - bare uppercase literals like `ADMIN`
185
+
186
+ ## `resource <name>:`
187
+
188
+ Example:
189
+
190
+ ```yaml
191
+ resource bookings:
192
+ model: Booking
193
+ api: /api/bookings
194
+ auth:
195
+ mode: authenticated
196
+ roles: [AGENT, ADMIN]
197
+ create:
198
+ rules: '@rules("./rules/booking-create")'
199
+ includes:
200
+ - field: passengers
201
+ fields: [name, seat]
202
+ update:
203
+ includes:
204
+ - field: passengers
205
+ fields: [id, name, seat]
206
+ workflow: '@flow("./workflows/booking-lifecycle")'
207
+ operations:
208
+ list: true
209
+ get: true
210
+ create: true
211
+ update: true
212
+ delete: true
213
+ ```
214
+
215
+ Current resource rules:
216
+
217
+ - `model` and `api` are required
218
+ - `auth.policy` may use `@fn("./policies/x")` or `@rules("./policies/x")`
219
+ - `create.rules` may use `@rules("./rules/x")`
220
+ - `create.rules` currently supports only:
221
+ - `eligibility`
222
+ - `validate`
223
+ - `create.rules` rejects:
224
+ - `allow/deny`
225
+ - `derive`
226
+ - `create.includes` / `update.includes` are the current one-level aggregate-root nested-write slice
227
+ - they accept only direct `hasMany(Target, by: field)` relations
228
+ - child `fields:` must belong to the target model
229
+ - the inverse `by:` field is seeded automatically and must not be listed again
230
+ - child `fields:` may currently use scalar, enum, or `belongsTo(...)`
231
+ - child `fields:` may not use `hasMany(...)`
232
+ - `update.includes` one-level diff semantics are:
233
+ - child items with `id` update existing children under the parent
234
+ - child items without `id` create new children
235
+ - omitted existing children are deleted
236
+
237
+ Current `workflow:` rules:
238
+
239
+ - `workflow:` is optional and must use `@flow("./workflows/x")`
240
+ - extensionless logical ids are preferred for `workflow:`
241
+ - the linked workflow `model` must match the resource model
242
+ - the linked workflow `field` must point to an `enum(...)` field on that model
243
+ - the linked workflow must declare every enum value from that field, and every declared workflow
244
+ state must exist in that enum
245
+ - generated workflow mutation paths add transition enforcement endpoints and preserve state on normal
246
+ update payloads
247
+
248
+ ## Transaction Rule
249
+
250
+ Generated backend write paths are transactional by default in the implemented targets:
251
+
252
+ - `resource create`
253
+ - `resource update`
254
+ - `resource delete`
255
+ - nested `create.includes` / `update.includes`
256
+ - workflow-linked create/update/transition paths
257
+
258
+ Current target behavior:
259
+
260
+ - Spring -> `@Transactional`
261
+ - FastAPI -> one generated `Session` commit/rollback boundary
262
+
263
+ Authors do not need and should not invent `transactional: true` in source DSL.
264
+
265
+ Custom escape hatches keep their own transaction boundary:
266
+
267
+ - `@fn(...)` is responsible for itself
268
+ - `@sql(...)` is read-model-only and read-only today
269
+
270
+ ## Backend Escape Hatches
271
+
272
+ Use escape hatches only when the current `.api.loj` slice cannot express the behavior directly.
273
+
274
+ ### `readModel handler: '@fn("./handlers/x")'`
275
+
276
+ Use `@fn(...)` for target-local query logic that is too specific for the shared read-model slice.
277
+
278
+ Rules:
279
+
280
+ - the handler file is a target-language function-body snippet, not a full controller/service/module
281
+ - extensionless logical ids are preferred
282
+ - Spring resolves to `.java`
283
+ - FastAPI resolves to `.py`
284
+
285
+ Current snippet contract:
286
+
287
+ - Spring executes inside:
288
+ - `List<ReadModelResult> execute(ReadModelInput input, PolicyPrincipal principal)`
289
+ - FastAPI executes inside:
290
+ - `def execute(db: Session, input: ReadModelInput, principal: AuthenticatedUser | None) -> list[ReadModelResult]`
291
+
292
+ Current file-shape rule:
293
+
294
+ - backend `@fn(...)` read-model handlers are function-body snippets
295
+ - do not include package declarations, imports, class declarations, or outer function declarations
296
+ - write only the body that runs inside the generated adapter function
297
+
298
+ Keep handler code target-local:
299
+
300
+ - use repositories / ORM / raw SQL as needed inside the snippet
301
+ - do not try to invent backend-family query-builder syntax around it
302
+
303
+ ### `readModel handler: '@sql("./queries/x")'`
304
+
305
+ Use `@sql(...)` only for narrow read-only read-model handlers.
306
+
307
+ Rules:
308
+
309
+ - file-backed only; do not inline large SQL strings into `.api.loj`
310
+ - read-only `SELECT` / `WITH` only
311
+ - no procedures, `CALL`, `EXEC`, or write SQL
312
+ - alias result columns to the declared `result:` field names
313
+ - Spring gets a generated `NamedParameterJdbcTemplate` adapter
314
+ - FastAPI gets a generated `sqlalchemy.text(...)` adapter
315
+
316
+ ### `auth.policy: '@fn(...)'`
317
+
318
+ Use `@fn(...)` for target-local auth decisions that cannot be expressed by `mode` / `roles` or the
319
+ current `.rules.loj` slice.
320
+
321
+ Keep it narrow:
322
+
323
+ - it is an additional policy check, not a replacement for the whole resource contract
324
+ - it keeps its own local transaction/runtime behavior
325
+ - policy snippets are also function-body snippets in the current backend-family targets
326
+ - do not include package/import/class/function declarations there either
327
+
328
+ ## Commands
329
+
330
+ - `sdsl validate <entry.api.loj>`
331
+ - `sdsl build <entry.api.loj> --out-dir <dir>`
332
+
333
+ Use `loj.project.yaml` instead when you need project-shell database/runtime profiles, auto-provision,
334
+ shutdown/probe/cors/base-path handling, or coordinated `dev/status/doctor`.
335
+
336
+ ## Guardrails
337
+
338
+ - Do not invent generic query composition or generic SQL DSL around `readModel`.
339
+ - Do not move database vendors or server runtime/deploy settings into `.api.loj`.
340
+ - Do not leak Spring/FastAPI framework vocabulary into source DSL.