backend-skeleton 1.2.0 → 1.4.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 (39) hide show
  1. package/README.md +100 -1
  2. package/bin/bskel.mjs +687 -6
  3. package/contracts/csv.mjs +100 -0
  4. package/handles/providers/java-spring/rules.mjs +143 -0
  5. package/handles/providers/java-spring/templates/EnforceRules.java.tmpl +44 -0
  6. package/handles/providers/java-spring/templates/RuleCheck.java.tmpl +215 -0
  7. package/handles/providers/java-spring/templates/RuleEnforcementAspect.java.tmpl +132 -0
  8. package/handles/providers/java-spring/templates/RuleSetLoader.java.tmpl +138 -0
  9. package/handles/providers/python-fastapi/rules.mjs +133 -0
  10. package/handles/providers/python-fastapi/templates/enforce_rules.py.tmpl +128 -0
  11. package/handles/providers/python-fastapi/templates/rule_check.py.tmpl +178 -0
  12. package/handles/providers/python-fastapi/templates/rule_set.py.tmpl +59 -0
  13. package/handles/providers/typescript-express/rules.mjs +129 -0
  14. package/handles/providers/typescript-express/templates/enforceRules.ts.tmpl +93 -0
  15. package/handles/providers/typescript-express/templates/ruleCheck.ts.tmpl +209 -0
  16. package/handles/providers/typescript-express/templates/ruleSet.ts.tmpl +61 -0
  17. package/lib/cli.mjs +99 -1
  18. package/lib/doctor.mjs +11 -10
  19. package/lib/gate-definitions.mjs +27 -1
  20. package/lib/workflow.mjs +15 -0
  21. package/new/index.mjs +20 -0
  22. package/package.json +6 -2
  23. package/patterns/schema.sql +18 -0
  24. package/patterns/store.mjs +122 -0
  25. package/rules/compile.mjs +433 -0
  26. package/rules/derived.mjs +87 -0
  27. package/rules/diagnostics.mjs +147 -0
  28. package/rules/store.mjs +141 -0
  29. package/rules/vocabulary.mjs +172 -0
  30. package/scanners/adapters/_express-shared.mjs +7 -9
  31. package/scanners/adapters/java-spring.mjs +7 -9
  32. package/scanners/adapters/python-fastapi.mjs +7 -9
  33. package/scanners/db/erd.mjs +0 -0
  34. package/scanners/index.mjs +70 -17
  35. package/scanners/render.mjs +12 -3
  36. package/scanners/text-util.mjs +22 -0
  37. package/schemas/feature-rules.schema.json +139 -0
  38. package/schemas/pattern-record.schema.json +19 -0
  39. package/schemas/scan-report.schema.json +6 -2
package/README.md CHANGED
@@ -32,9 +32,13 @@ check for a specific failure mode found the same way — see `DECISIONS.md` for
32
32
 
33
33
  - [Status: 1.0.0](#status-100)
34
34
  - [Quickstart](#quickstart)
35
+ - [Try it in 10 seconds](#try-it-in-10-seconds)
36
+ - [The gated workflow](#the-gated-workflow)
35
37
  - [Starting from nothing (greenfield)](#starting-from-nothing-greenfield)
36
38
  - [Publishing a feature's contract as OpenAPI (optional)](#publishing-a-features-contract-as-openapi-optional)
39
+ - [A CSV table of a feature's contract (optional)](#a-csv-table-of-a-features-contract-optional)
37
40
  - [Database schema (optional)](#database-schema-optional)
41
+ - [An ERD of your database schema (optional)](#an-erd-of-your-database-schema-optional)
38
42
  - [Applying DDL to a live database (optional)](#applying-ddl-to-a-live-database-optional)
39
43
  - [Declaring field-to-field dependencies (optional)](#declaring-field-to-field-dependencies-optional)
40
44
  - [Patching a config file (optional)](#patching-a-config-file-optional)
@@ -84,6 +88,21 @@ production -- which still splits the same way it always has:
84
88
 
85
89
  ## Quickstart
86
90
 
91
+ ### Try it in 10 seconds
92
+
93
+ ```bash
94
+ npm install -g backend-skeleton # or: npx backend-skeleton <command>
95
+ cd <any-existing-repo> # must be a git repository -- that's the only requirement
96
+ bskel scan # zero flags: every module/controller/entity/enum this repo's
97
+ # adapter can see, unscored -- no preflight, no feature, no
98
+ # files written, no gate touched
99
+ ```
100
+
101
+ That's a read-only look, not the gated workflow — for real feature work (collision-checked against
102
+ a specific idea, contract-gated, codegen), see below.
103
+
104
+ ### The gated workflow
105
+
87
106
  ```bash
88
107
  npm install -g backend-skeleton # or: npx backend-skeleton <command>
89
108
  cd <target-repo> # must be a git repository
@@ -193,6 +212,27 @@ bskel new --stack fastapi --slug my-service \
193
212
  The full parameter list, the measured API-validation matrix behind that split, and the warning
194
213
  behaviour are in `D-greenfield-parameters` in `DECISIONS.md`.
195
214
 
215
+ #### Remembering your own conventions across projects (optional)
216
+
217
+ If you start several projects with the same conventions, `bskel new` can record them into a
218
+ database **you own** -- never bskel's own state, never a shared store:
219
+
220
+ ```bash
221
+ export MY_PATTERNS=postgres://localhost/my_patterns # once: run patterns/schema.sql against it
222
+
223
+ bskel new --stack spring --slug billing \
224
+ --java-version 21 --group-id com.acme --dependencies web,data-jpa,validation,flyway \
225
+ --record-pattern --pattern-database-url-env MY_PATTERNS
226
+
227
+ bskel pattern suggest --stack spring --pattern-database-url-env MY_PATTERNS
228
+ ```
229
+
230
+ `pattern suggest` prints what you've recorded, with per-value frequency, and a ready-to-paste
231
+ command line at the bottom -- it never runs `bskel new` for you and `bskel new` has no flag that
232
+ would accept a suggestion as a default. Every value in a generated project is still one you typed
233
+ in that invocation. Omitting `--record-pattern`/`--pattern-database-url-env` leaves `bskel new`
234
+ exactly as it is today. See `D-pattern-accrual` in `DECISIONS.md`.
235
+
196
236
  ### Publishing a feature's contract as OpenAPI (optional)
197
237
 
198
238
  ```bash
@@ -232,6 +272,31 @@ contract's paths don't reflect (`--allow-unprefixed` overrides), and stamps ever
232
272
  reconciling a contract against its own export would make it confirm itself. See `D-openapi-export`
233
273
  in `DECISIONS.md`.
234
274
 
275
+ ### A CSV table of a feature's contract (optional)
276
+
277
+ ```bash
278
+ bskel contract export-csv --feature 001-organization-management --out organization.csv
279
+ ```
280
+
281
+ One row per operation, opened by someone who will never read a JSON Schema — a PM reviewing scope,
282
+ a lead deciding whether a partial contract is good enough to waive. Fourteen columns, always, in
283
+ the same order: `operation_id, verb, path, path_params, path_params_unverified, body,
284
+ request_body_required, request_body_fields, response_fields, error_fields, provenance, summary,
285
+ tags, security`.
286
+
287
+ **Every column is always present, even when every row leaves it blank** — a scan-only contract
288
+ (no `--openapi-file`) never states `summary`/`tags`/`security`, and the blank columns *are* that
289
+ finding, not something to hide: `export-csv` prints exactly which columns are empty for every
290
+ operation, and why, on stderr. Dropping an empty column would make the file's own shape depend on
291
+ its content, so it never does.
292
+
293
+ **Unlike `contract export`, this command is deliberately UNGATED** — it works even when the
294
+ `contract` gate hasn't passed yet, because its single most valuable moment is reviewing a partial
295
+ contract to decide whether to waive it. An unreflected global path-prefix signal (the thing
296
+ `contract export` hard-refuses on) downgrades to a stderr warning here instead. `--bom` prepends a
297
+ UTF-8 byte-order mark for Excel-on-Windows, which otherwise mangles non-ASCII text; omit it for
298
+ `pandas`/`csv.DictReader`/`diff`, which don't want one. See `D-contract-csv` in `DECISIONS.md`.
299
+
235
300
  ### Database schema (optional)
236
301
 
237
302
  `bskel scan --db` additionally scans Flyway/Liquibase migration files (local only, no network).
@@ -249,6 +314,40 @@ report also carries `generated_at` — when the underlying data was actually cap
249
314
  be judged for staleness rather than trusted blindly. See `D-cross-feature-fk-inference` in
250
315
  `DECISIONS.md`.
251
316
 
317
+ ### An ERD of your database schema (optional)
318
+
319
+ ```bash
320
+ bskel db erd --database-url-env BSKEL_DB_URL --schema public --out schema.mmd
321
+ ```
322
+
323
+ A Mermaid `erDiagram` of the database — paste it straight into a GitHub/GitLab/Notion/Obsidian
324
+ markdown file (fence it in \`\`\`mermaid) or [mermaid.live](https://mermaid.live) and it renders
325
+ with no extra tooling. Works two ways:
326
+
327
+ - **With `--database-url-env`**: a real, live Postgres introspection — full column types,
328
+ nullability, and primary/foreign keys.
329
+ - **Without it**: falls back to scanning Flyway/Liquibase `.sql` migration files (no network, no
330
+ credentials needed) — a real but **degraded** diagram, clearly marked as such in the file itself:
331
+ every column types as `unknown`, no `PK` badge appears anywhere, and a header block spells out
332
+ exactly what's missing. Useful for evaluating the tool on a repo you don't have DB credentials
333
+ for yet.
334
+
335
+ Two things this diagram deliberately does **not** guess:
336
+
337
+ - **Composite foreign keys.** Postgres's own `information_schema` doesn't retain which source
338
+ column pairs with which target column once a foreign key spans more than one column — the raw
339
+ data is a cross product that can include pairs that were never declared. Rather than draw a wrong
340
+ relationship line, a composite FK collapses to one line labeled with all its source columns
341
+ joined by `+`, with the ambiguity spelled out in a `%%` comment above it. (Composite *primary*
342
+ keys have no such problem and render fully.)
343
+ - **1:1 vs 1:N.** The child side of every relationship is drawn as "zero or more," never "exactly
344
+ one" — telling those apart needs a UNIQUE constraint check this tool doesn't perform. The header
345
+ says so.
346
+
347
+ Whole-schema only in this version (no `--feature`/`--tables` filtering yet) — for a very large
348
+ schema, `db erd` prints a note above 40 entities rather than silently producing an unreadable
349
+ diagram. See `D-db-erd` in `DECISIONS.md`.
350
+
252
351
  ### Applying DDL to a live database (optional)
253
352
 
254
353
  `bskel patch propose --kind ddl-apply` extends the same propose/approve/apply/rollback lifecycle
@@ -385,7 +484,7 @@ below).
385
484
  |---|---|---|
386
485
  | Node.js | `>=18` | ES2022 (`Object.hasOwn`) + ESM top-level `await` — nothing newer is used anywhere in the runtime code (verified by grep across every recent-ES-addition pattern; see `D-npm-packaging` in `DECISIONS.md`) |
387
486
  | git | required | every gate is git-state-derived |
388
- | [ripgrep](https://github.com/BurntSushi/ripgrep) (`rg`) | required for `scan`/`handles` | every scanner adapter shells out to it directly, and throws (not degrades) if it's missing |
487
+ | [ripgrep](https://github.com/BurntSushi/ripgrep) (`rg`) | required for `scan`/`handles` | every scanner adapter shells out to it at `detect()` time behind a blanket try/catch — traced live, missing `rg` does NOT throw, it silently makes every real adapter detect nothing (degrades to the low-confidence `generic-grep` fallback). `bskel scan`'s report now carries a `rg_available: false` field plus an explicit `unknowns` warning whenever this happens, so it stays distinguishable from a genuinely-unrecognized repo — see `D-zero-config-scan` in `DECISIONS.md` |
389
488
  | `gh` (GitHub CLI) | optional | only used for `preflight`'s 3-way default-branch cross-check; already soft-guarded, never a hard requirement |
390
489
  | `python3` | optional | only needed to run this repository's own cross-language codec test — `bskel` itself never invokes `python3` |
391
490
  | a build wrapper (`gradlew`/`pom.xml`+`mvnw`/`package.json`) | optional | only `bskel verify --build` needs one; `handles emit` never compiles anything itself |