backend-skeleton 1.0.0-beta.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/LICENSE +21 -0
- package/README.md +284 -0
- package/bin/bskel.mjs +2384 -0
- package/contracts/completeness.mjs +176 -0
- package/contracts/emit.mjs +287 -0
- package/contracts/export.mjs +325 -0
- package/contracts/openapi.mjs +869 -0
- package/contracts/validate.mjs +147 -0
- package/handles/_engine.mjs +281 -0
- package/handles/codec.mjs +119 -0
- package/handles/conformance.mjs +74 -0
- package/handles/providers/java-spring/ast-bridge.mjs +59 -0
- package/handles/providers/java-spring/ast-helper/build.gradle +34 -0
- package/handles/providers/java-spring/ast-helper/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/handles/providers/java-spring/ast-helper/gradle/wrapper/gradle-wrapper.properties +9 -0
- package/handles/providers/java-spring/ast-helper/gradlew +248 -0
- package/handles/providers/java-spring/ast-helper/gradlew.bat +82 -0
- package/handles/providers/java-spring/ast-helper/settings.gradle +1 -0
- package/handles/providers/java-spring/ast-helper/src/main/java/com/backendskeleton/asthelper/Main.java +178 -0
- package/handles/providers/java-spring/emit.mjs +232 -0
- package/handles/providers/java-spring/patch-strategy.mjs +229 -0
- package/handles/providers/java-spring/plan.mjs +377 -0
- package/handles/providers/java-spring/templates/HandleAspect.java.tmpl +125 -0
- package/handles/providers/java-spring/templates/HandleCodec.java.tmpl +150 -0
- package/handles/providers/java-spring/templates/HandleController.java.tmpl +177 -0
- package/handles/providers/java-spring/templates/HandleRegistry.java.tmpl +107 -0
- package/handles/providers/java-spring/templates/HandleRegistryRepository.java.tmpl +8 -0
- package/handles/providers/java-spring/templates/HandleService.java.tmpl +95 -0
- package/handles/providers/java-spring/templates/HandleSnapshot.java.tmpl +75 -0
- package/handles/providers/java-spring/templates/HandleSnapshotRepository.java.tmpl +20 -0
- package/handles/providers/java-spring/templates/RecordHandleSnapshot.java.tmpl +50 -0
- package/handles/providers/java-spring/templates/ResourceResolver.java.tmpl +50 -0
- package/handles/providers/java-spring/templates/ResourceResolverStub.java.tmpl +77 -0
- package/handles/providers/java-spring/templates/migration.sql.tmpl +34 -0
- package/handles/providers/java-spring.mjs +21 -0
- package/handles/providers/python-fastapi/emit.mjs +171 -0
- package/handles/providers/python-fastapi/plan.mjs +186 -0
- package/handles/providers/python-fastapi/templates/__init__.py.tmpl +1 -0
- package/handles/providers/python-fastapi/templates/codec.py.tmpl +122 -0
- package/handles/providers/python-fastapi/templates/handle_service.py.tmpl +96 -0
- package/handles/providers/python-fastapi/templates/migration.sql.tmpl +35 -0
- package/handles/providers/python-fastapi/templates/record_snapshot.py.tmpl +155 -0
- package/handles/providers/python-fastapi/templates/registry.py.tmpl +37 -0
- package/handles/providers/python-fastapi/templates/resolver.py.tmpl +59 -0
- package/handles/providers/python-fastapi/templates/resolvers_init.py.tmpl +13 -0
- package/handles/providers/python-fastapi/templates/router.py.tmpl +140 -0
- package/handles/providers/python-fastapi/templates/tables.py.tmpl +66 -0
- package/handles/providers/python-fastapi.mjs +22 -0
- package/handles/providers/typescript-express/emit.mjs +128 -0
- package/handles/providers/typescript-express/plan.mjs +234 -0
- package/handles/providers/typescript-express/templates/codec.ts.tmpl +116 -0
- package/handles/providers/typescript-express/templates/registry.ts.tmpl +39 -0
- package/handles/providers/typescript-express/templates/resolver.ts.tmpl +55 -0
- package/handles/providers/typescript-express/templates/resolvers_index.ts.tmpl +11 -0
- package/handles/providers/typescript-express/templates/router.ts.tmpl +122 -0
- package/handles/providers/typescript-express.mjs +20 -0
- package/handles/registry.mjs +90 -0
- package/lib/cli.mjs +430 -0
- package/lib/doctor.mjs +200 -0
- package/lib/exit-codes.mjs +67 -0
- package/lib/featureid.mjs +55 -0
- package/lib/featurelifecycle.mjs +205 -0
- package/lib/fsutil.mjs +50 -0
- package/lib/gate-definitions.mjs +293 -0
- package/lib/gates.mjs +263 -0
- package/lib/handles-manifest.mjs +92 -0
- package/lib/lock.mjs +68 -0
- package/lib/patch-approvals.mjs +56 -0
- package/lib/paths.mjs +21 -0
- package/lib/repo.mjs +44 -0
- package/lib/schema-validate.mjs +56 -0
- package/lib/state.mjs +124 -0
- package/lib/template.mjs +35 -0
- package/lib/verify.mjs +206 -0
- package/lib/workflow.mjs +142 -0
- package/new/fastapi.mjs +165 -0
- package/new/index.mjs +62 -0
- package/new/params.mjs +233 -0
- package/new/spring.mjs +198 -0
- package/new/templates/fastapi/README.md +26 -0
- package/new/templates/fastapi/app/__init__.py +0 -0
- package/new/templates/fastapi/app/main.py +8 -0
- package/new/templates/fastapi/gitignore +6 -0
- package/new/templates/fastapi/pyproject.toml +14 -0
- package/package.json +50 -0
- package/scanners/adapters/_express-shared.mjs +238 -0
- package/scanners/adapters/_java-spring-analyzer.mjs +273 -0
- package/scanners/adapters/generic-grep.mjs +128 -0
- package/scanners/adapters/java-spring.mjs +301 -0
- package/scanners/adapters/javascript-express.mjs +422 -0
- package/scanners/adapters/python-fastapi.mjs +348 -0
- package/scanners/adapters/typescript-express.mjs +299 -0
- package/scanners/capabilities.mjs +90 -0
- package/scanners/conformance.mjs +59 -0
- package/scanners/db/introspect.mjs +109 -0
- package/scanners/db/migrations.mjs +126 -0
- package/scanners/index.mjs +281 -0
- package/scanners/registry.mjs +130 -0
- package/scanners/render.mjs +136 -0
- package/scanners/text-util.mjs +8 -0
- package/schemas/adapter.schema.json +23 -0
- package/schemas/agent-envelope.schema.json +21 -0
- package/schemas/contract-resolution.schema.json +28 -0
- package/schemas/feature-contract.schema.json +78 -0
- package/schemas/feature-index.schema.json +25 -0
- package/schemas/feature.schema.json +17 -0
- package/schemas/gate-event.schema.json +19 -0
- package/schemas/handles-plan.schema.json +31 -0
- package/schemas/handles-provider.schema.json +26 -0
- package/schemas/patch-approvals.schema.json +28 -0
- package/schemas/scan-report.schema.json +102 -0
- package/schemas/stack-choice.schema.json +89 -0
- package/schemas/stack-record.schema.json +20 -0
- package/schemas/state.schema.json +43 -0
- package/scripts/preflight-base-ref.sh +226 -0
- package/stack/apply.mjs +159 -0
- package/stack/bootstrap/_lib.sh +73 -0
- package/stack/bootstrap/ngrok.sh +90 -0
- package/stack/catalog/ngrok.yml +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 popixoxipop
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# backend-skeleton
|
|
2
|
+
|
|
3
|
+
Spec-driven backend scaffolding for brownfield (and greenfield) Java/Spring Boot, Python/FastAPI,
|
|
4
|
+
and TypeScript/JavaScript Express repos: a brownfield-collision gate before any spec/plan step,
|
|
5
|
+
feature_id-scoped machine-readable contracts, UUID-addressable field handles, and stack-choice
|
|
6
|
+
(e.g. ngrok) wiring — all enforced by disk `content-hash` gates, not prompt instructions a future
|
|
7
|
+
session could ignore.
|
|
8
|
+
|
|
9
|
+
`bskel` exists because a previous ad-hoc agent-driven scaffolding attempt branched a worktree 658
|
|
10
|
+
commits behind the real default branch and never noticed. Every gate in this tool is a regression
|
|
11
|
+
check for a specific failure mode found the same way — see `DECISIONS.md` for the full record.
|
|
12
|
+
|
|
13
|
+
## Status: beta
|
|
14
|
+
|
|
15
|
+
This is the first public release, and it's deliberately labeled a beta rather than `1.0.0`
|
|
16
|
+
stable. Split by actual maturity, not by feature list:
|
|
17
|
+
|
|
18
|
+
- **`scan`, `contract` (including `export`), and `new`** are the most exercised paths — real,
|
|
19
|
+
measured verification against a real production Spring Boot repo (see `DECISIONS.md`), plus a
|
|
20
|
+
synthetic fixture corpus for every adapter, run in CI on every change.
|
|
21
|
+
- **`handles`** (the UUID-addressable resolver/codec/router codegen) is functionally complete and
|
|
22
|
+
tested the same way, but has never been deployed to a real production repo, and two named gaps
|
|
23
|
+
stay open specifically because of that: generated `fetch()`/`patch()` paths never check
|
|
24
|
+
`HandleRegistry` for revocation, and generated authorization is inferred from a single
|
|
25
|
+
`@PreAuthorize(hasRole(...))` shape rather than a real policy contract. Both are tracked as `O3`
|
|
26
|
+
and `O5` in `CATALOG.md`, explicitly deferred until handles are actually used somewhere real --
|
|
27
|
+
treat `handles emit`'s output as a scaffold to finish by hand, not a production-ready subsystem,
|
|
28
|
+
until that work lands.
|
|
29
|
+
|
|
30
|
+
Version numbers, install instructions, and a real feedback path will firm up as this gets used
|
|
31
|
+
against more real repos.
|
|
32
|
+
|
|
33
|
+
## Quickstart
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g backend-skeleton@beta # or: npx backend-skeleton@beta <command>
|
|
37
|
+
cd <target-repo> # must be a git repository
|
|
38
|
+
|
|
39
|
+
bskel doctor # what's on PATH, which scanner adapter detects this repo, and why
|
|
40
|
+
bskel preflight # confirms HEAD is actually based on the real default branch,
|
|
41
|
+
# not a stale/abandoned one -- required before anything else
|
|
42
|
+
|
|
43
|
+
bskel feature init --slug organization-management
|
|
44
|
+
bskel scan --feature 001-organization-management --terms organization
|
|
45
|
+
# brownfield-collision scan; refuses to proceed silently if this
|
|
46
|
+
# module already exists elsewhere in the codebase
|
|
47
|
+
bskel scan disposition --feature 001-organization-management --mode reuse --note "..."
|
|
48
|
+
# required once scan finds a collision/adjacent match
|
|
49
|
+
|
|
50
|
+
bskel contract emit --feature 001-organization-management
|
|
51
|
+
# feature_id-scoped JSON Schema contract, from real source annotations
|
|
52
|
+
bskel handles plan --feature 001-organization-management
|
|
53
|
+
bskel handles emit --feature 001-organization-management
|
|
54
|
+
# UUID-addressable field handles + generated resolver code
|
|
55
|
+
|
|
56
|
+
bskel verify --feature 001-organization-management --build
|
|
57
|
+
# aggregates every gate's current status; --build also runs the
|
|
58
|
+
# target repo's own build wrapper (gradlew/mvnw/npm), if present
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Starting from nothing (greenfield)
|
|
62
|
+
|
|
63
|
+
Every command above assumes an existing Spring Boot or FastAPI repo. If you don't have one yet:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bskel new --stack spring --slug my-service # calls start.spring.io (network required), or:
|
|
67
|
+
bskel new --stack fastapi --slug my-service # a local starter template, no network call
|
|
68
|
+
|
|
69
|
+
cd my-service
|
|
70
|
+
# create a remote you own and push to it (e.g. `gh repo create --private --source=. --push`),
|
|
71
|
+
# then: git remote set-head origin --auto
|
|
72
|
+
bskel preflight # now resolvable -- picks up from the Quickstart above
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`bskel new` deliberately never creates a remote itself and never auto-chains into `preflight` --
|
|
76
|
+
`preflight` requires a real `origin` remote with a resolvable default branch, which a brand-new
|
|
77
|
+
local-only repo doesn't have yet. See `D-greenfield-bootstrap` in `DECISIONS.md`.
|
|
78
|
+
|
|
79
|
+
Both stacks accept `--name`, `--description` and `--project-version` (the *generated project's* own
|
|
80
|
+
version -- `--version` is a global flag that prints `bskel`'s). Beyond that the parameters differ,
|
|
81
|
+
because the two ecosystems do:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
bskel new --stack spring --slug my-service \
|
|
85
|
+
--group-id com.acme --artifact-id billing --package-name com.acme.billing \
|
|
86
|
+
--java-version 21 --packaging war --add-dependencies actuator,postgresql
|
|
87
|
+
|
|
88
|
+
bskel new --stack fastapi --slug my-service \
|
|
89
|
+
--python-version 3.12 --port 9000 --license MIT --database postgres
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- **`--add-dependencies` extends** the baseline (`web, data-jpa, security, validation, lombok`).
|
|
93
|
+
**`--dependencies` REPLACES it** -- and if the result drops `web`, `data-jpa` or `validation`, you
|
|
94
|
+
get a specific stderr warning naming what stops working downstream, then it scaffolds anyway.
|
|
95
|
+
- **`--group-id`/`--package-name`/`--artifact-id` are validated locally** against the Java package
|
|
96
|
+
grammar. That isn't belt-and-braces: `start.spring.io` accepts `groupId=com.new` (a reserved word)
|
|
97
|
+
and `groupId=has space` with HTTP 200 and hands back a project that cannot compile.
|
|
98
|
+
- **`--java-version` is checked against `start.spring.io`'s own live metadata**, fetched on demand
|
|
99
|
+
only when you pass a non-default value, never cached to disk -- for the same reason: `javaVersion=99`
|
|
100
|
+
returns HTTP 200 and writes `JavaLanguageVersion.of(99)` straight into `build.gradle`.
|
|
101
|
+
- **`--database` pins a driver and nothing else** -- no engine, session or connection code is
|
|
102
|
+
generated, because that would be `bskel` inventing your domain.
|
|
103
|
+
- `--type`, `--language` and `--boot-version` are deliberately **refused** with a specific reason
|
|
104
|
+
each (Maven/Kotlin scaffolds break this tool's own scanner and codegen assumptions; a bad
|
|
105
|
+
`bootVersion` gets an unusable HTTP 500). Run `bskel new --stack spring --type maven-project` to
|
|
106
|
+
see the actual explanation.
|
|
107
|
+
|
|
108
|
+
The full parameter list, the measured API-validation matrix behind that split, and the warning
|
|
109
|
+
behaviour are in `D-greenfield-parameters` in `DECISIONS.md`.
|
|
110
|
+
|
|
111
|
+
### Publishing a feature's contract as OpenAPI (optional)
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
bskel contract export --feature 001-organization-management --out openapi/organization.json
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Renders an already-emitted, gate-passing contract as a standalone **OpenAPI 3.1** document — the
|
|
118
|
+
inverse of `contract emit --openapi-file`. Useful for a Swagger UI page scoped to one feature
|
|
119
|
+
instead of the whole repo, a client generator that can't follow `$ref` (an exported document has
|
|
120
|
+
none), or a mock server for one feature's operations.
|
|
121
|
+
|
|
122
|
+
It is a **deliberately lossy, narrow projection, and it says so**: the contract carries no query or
|
|
123
|
+
header parameters, no security requirements, no summaries or tags, and no per-status responses, so
|
|
124
|
+
none of those appear — every omission is disclosed both in prose (`info.description`) and
|
|
125
|
+
machine-readably (`info.x-bskel-omitted`). Nothing is invented to fill a gap: `security` is omitted
|
|
126
|
+
rather than emitted as `[]` (an empty array would positively claim no authentication is required),
|
|
127
|
+
and an operation whose body shape the contract doesn't know gets a JSON media-type entry with no
|
|
128
|
+
schema rather than a fabricated one.
|
|
129
|
+
|
|
130
|
+
Export refuses a zero-operation contract, refuses when the scan found a global path prefix the
|
|
131
|
+
contract's paths don't reflect (`--allow-unprefixed` overrides), and stamps every document with an
|
|
132
|
+
`x-bskel-generated` marker that `contract emit --openapi-file` then refuses to read back in —
|
|
133
|
+
reconciling a contract against its own export would make it confirm itself. See `D-openapi-export`
|
|
134
|
+
in `DECISIONS.md`.
|
|
135
|
+
|
|
136
|
+
### Database schema (optional)
|
|
137
|
+
|
|
138
|
+
`bskel scan --db` additionally scans Flyway/Liquibase migration files (local only, no network).
|
|
139
|
+
Add `--database-url-env <NAME>` (naming an environment variable you've already exported, never
|
|
140
|
+
read from `.env`) for live, read-only Postgres introspection (`information_schema`/`pg_catalog`,
|
|
141
|
+
inside a `BEGIN TRANSACTION READ ONLY`) and a source-vs-live drift report. Both are informational
|
|
142
|
+
additions to the scan report — neither blocks any gate. See `D-db-schema-plane` in `DECISIONS.md`.
|
|
143
|
+
|
|
144
|
+
Every command is read-only until you explicitly run one of the mutating steps above — `bskel
|
|
145
|
+
status`/`bskel next` (no arguments needed) tell you which gate is next and print the exact
|
|
146
|
+
copy-pasteable command for it, without touching anything.
|
|
147
|
+
|
|
148
|
+
The full gated workflow, what each phase writes, and every flag is documented in `SKILL.md`
|
|
149
|
+
(present in this repository, not in the installed npm package — see "What ships in the package"
|
|
150
|
+
below).
|
|
151
|
+
|
|
152
|
+
## Compatibility
|
|
153
|
+
|
|
154
|
+
| Requirement | Constraint | Why |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| 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`) |
|
|
157
|
+
| git | required | every gate is git-state-derived |
|
|
158
|
+
| [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 |
|
|
159
|
+
| `gh` (GitHub CLI) | optional | only used for `preflight`'s 3-way default-branch cross-check; already soft-guarded, never a hard requirement |
|
|
160
|
+
| `python3` | optional | only needed to run this repository's own cross-language codec test — `bskel` itself never invokes `python3` |
|
|
161
|
+
| a build wrapper (`gradlew`/`pom.xml`+`mvnw`/`package.json`) | optional | only `bskel verify --build` needs one; `handles emit` never compiles anything itself |
|
|
162
|
+
|
|
163
|
+
Run `bskel doctor` in any target repo to see exactly which of these it found, with a remediation
|
|
164
|
+
string for anything missing.
|
|
165
|
+
|
|
166
|
+
**Supported scanner adapters** (auto-selected by specificity, never hardcoded — see
|
|
167
|
+
`D-adapter-registry` in `DECISIONS.md`):
|
|
168
|
+
- `java-spring` — Spring Boot (`build.gradle`/`pom.xml` + `src/main/java`). Full capability set:
|
|
169
|
+
operation extraction, request-body detection, and a real codegen provider for `handles emit`.
|
|
170
|
+
- `python-fastapi` — FastAPI + SQLModel. Real codegen provider for `handles emit`; contract-grade
|
|
171
|
+
operation extraction is not supported (FastAPI generates operation ids at runtime) — pass a real
|
|
172
|
+
OpenAPI document via `--openapi-file` for a trustworthy contract.
|
|
173
|
+
- `typescript-express` — TypeScript + Express + TypeORM. Real codegen provider for `handles emit`
|
|
174
|
+
(entities come from `@Entity`/`@PrimaryGeneratedColumn`); no operation extraction — plain Express
|
|
175
|
+
has no operationId concept, so pass `--openapi-file` for a contract. See
|
|
176
|
+
`D-typescript-express-provider` in `DECISIONS.md`.
|
|
177
|
+
- `javascript-express` — plain-JavaScript ESM Express with **no ORM** (raw `mysql2`/`mariadb`),
|
|
178
|
+
including `serverless-http`/Lambda deployments. **Scanner only** — routes and their real absolute
|
|
179
|
+
paths are resolved through a full mount-graph walk, but every capability is honestly `false`:
|
|
180
|
+
there is no codegen provider, because raw SQL string literals carry no trustworthy
|
|
181
|
+
table/primary-key/column-allow-list metadata. See `D-javascript-express-adapter` in `DECISIONS.md`
|
|
182
|
+
for the measured reasoning.
|
|
183
|
+
- `generic-grep` — unconditional last-resort fallback (Express/Flask/FastAPI-shaped route
|
|
184
|
+
detection). Reconnaissance only, never contract-grade — always `confidence: "low"`, requires
|
|
185
|
+
`--accept-low-confidence` to proceed past a feature-scoped scan.
|
|
186
|
+
|
|
187
|
+
## Generated-file policy
|
|
188
|
+
|
|
189
|
+
`bskel handles emit` writes real Java/Python source into your repository. Two things are always
|
|
190
|
+
true about what it writes:
|
|
191
|
+
|
|
192
|
+
- **`fetch()` is wired to a real, existing, already-tested read-only service method** — never
|
|
193
|
+
hand-written business logic. It's generated only when a matching `<Entity>Service` method exists
|
|
194
|
+
and takes exactly the one resource UUID argument a resolver always passes (a mismatch there means
|
|
195
|
+
"no resolver generated", not "generate one and hope", since silently calling the wrong overload
|
|
196
|
+
can drop a required scoping argument — see `D-security-8` in `DECISIONS.md`).
|
|
197
|
+
- **`patchField()` is always a stub.** Real codebases mix at least three different partial-update
|
|
198
|
+
DTO conventions; guessing wrong would silently bypass real validation. A human finishes this by
|
|
199
|
+
hand, every time.
|
|
200
|
+
|
|
201
|
+
Reruns are safe by construction, not by convention (`D-handles-ownership` in `DECISIONS.md`):
|
|
202
|
+
safety is derived from the generated file's actual on-disk **content**, not from a manifest that
|
|
203
|
+
might be absent (a fresh checkout, CI, or a repo that doesn't commit `.sbf/`). A file that diverged
|
|
204
|
+
from what `bskel` generated (a hand-finished `patchField()`, someone else's edit) is never silently
|
|
205
|
+
overwritten — it reports a conflict, and the escape hatch (`--force --reason "..."`) is always
|
|
206
|
+
audited, never silent.
|
|
207
|
+
|
|
208
|
+
## Security model
|
|
209
|
+
|
|
210
|
+
`bskel` generates code that runs in production, so it was put through an adversarial security
|
|
211
|
+
review (Codex, security-only lens, independent of the build process) — 8 findings, all fixed, each
|
|
212
|
+
with an inline `D-security-N` comment at its exact location in the code. Highlights (full record,
|
|
213
|
+
including 3 additional defensive-hardening items, in `DECISIONS.md`'s "Security hardening pass"
|
|
214
|
+
section):
|
|
215
|
+
|
|
216
|
+
- **Prototype-pollution guards** everywhere a user-controlled string indexes a plain object
|
|
217
|
+
(`operation_id` values like `"constructor"`/`"__proto__"` are rejected, not silently resolved via
|
|
218
|
+
the prototype chain).
|
|
219
|
+
- **Path-traversal containment** on every stack-catalog-driven file write (`--choice`, and every
|
|
220
|
+
catalog entry's own declared template/path fields).
|
|
221
|
+
- **No predictable temp files, no silent permission downgrades** in the generated bootstrap
|
|
222
|
+
scripts that touch `.env` (`mktemp` + an unconditional `chmod 600`).
|
|
223
|
+
- **Authority derivation is per-method, not per-file** — a controller's first `@PreAuthorize` match
|
|
224
|
+
no longer silently applies to every resolver generated from that file; an unsupported annotation
|
|
225
|
+
shape (`hasAnyRole`, SpEL) fails closed to a `TODO_ROLE` placeholder rather than guessing.
|
|
226
|
+
- **Handle recovery cross-checks type/kind/pointer against the registry row**, not just the raw
|
|
227
|
+
UUID — the most severe finding: an attacker who controls the handle's `type` field could
|
|
228
|
+
otherwise request a different, more sensitive resource's snapshot history that happens to share
|
|
229
|
+
the same UUID.
|
|
230
|
+
|
|
231
|
+
## Troubleshooting
|
|
232
|
+
|
|
233
|
+
Start with `bskel doctor` — it names exactly which required tool is missing and why, or run `bskel
|
|
234
|
+
status`/`bskel next` to see which gate is currently blocking and the exact command to resolve it.
|
|
235
|
+
|
|
236
|
+
Every command shares one exit-code table (`lib/exit-codes.mjs`) and, with `--json`, an additive
|
|
237
|
+
diagnostic envelope on payload-less early exits — the number is the stable contract, `reason` in
|
|
238
|
+
the envelope is supplementary precision:
|
|
239
|
+
|
|
240
|
+
| Exit | Meaning |
|
|
241
|
+
|---|---|
|
|
242
|
+
| `0` | OK |
|
|
243
|
+
| `2` | a required gate hasn't passed yet, or a referenced resource/adapter/provider doesn't exist (`--json`'s `reason` field disambiguates which) |
|
|
244
|
+
| `3` | a gate is awaiting a disposition decision (`bskel scan disposition`/`bskel contract waive`) |
|
|
245
|
+
| `4` | a gate is stale — either an input actually changed, or (`preflight` only) the pass is simply too old |
|
|
246
|
+
| `10` | not inside a git repository |
|
|
247
|
+
| `11` | `preflight`: HEAD is behind the real default branch |
|
|
248
|
+
| `12` | `preflight`: the three independent sources for "what is the default branch" disagree, or none could be determined |
|
|
249
|
+
| `13` | `preflight`: uncommitted changes present (`--allow-dirty` to override) |
|
|
250
|
+
| `14` | bad arguments |
|
|
251
|
+
| `16` | a low-confidence scan was blocked (`--accept-low-confidence` to proceed anyway) |
|
|
252
|
+
| `17` | the selected adapter/provider doesn't support a capability the command needs |
|
|
253
|
+
| `18` | `preflight`: a `git fetch` was attempted and failed (`--offline` to accept a local-only verdict instead) |
|
|
254
|
+
|
|
255
|
+
Common cases:
|
|
256
|
+
- **`preflight` fails with `STALE_BASE`**: your branch really is behind — `git worktree add
|
|
257
|
+
<path> -b <branch> origin/<default-branch>` (or rebase in place), then re-run.
|
|
258
|
+
- **`scan` exits `16`**: the scanner fell back to `generic-grep` (low confidence, no real parser).
|
|
259
|
+
If this repo actually is Java/Spring or Python/FastAPI-shaped, run `bskel doctor` first — it
|
|
260
|
+
explains exactly why the real adapter didn't detect it, rather than reflexively passing
|
|
261
|
+
`--accept-low-confidence`.
|
|
262
|
+
- **`handles emit`/`contract emit` exits `17`**: the adapter that scanned this repo doesn't
|
|
263
|
+
declare the capability that command needs (e.g. `generic-grep` never declares `codegen.handles`
|
|
264
|
+
— there's no codegen provider for a route-pattern-only stack). `bskel doctor` lists every
|
|
265
|
+
installed adapter's declared capabilities.
|
|
266
|
+
- **A previously-passed `preflight` now reports stale with `ttl_expired`**: passes expire after 30
|
|
267
|
+
minutes by default (data-derived, see `D-preflight-freshness` in `DECISIONS.md`) — re-run
|
|
268
|
+
`bskel preflight`, or pass `--max-age-minutes 0` to disable the TTL for a deliberately
|
|
269
|
+
long-running or offline session.
|
|
270
|
+
|
|
271
|
+
The full exit-code/`reason` taxonomy, global flags (`--help`/`--version`/`--json`/`--quiet`), and
|
|
272
|
+
the complete gated-workflow reference live in `SKILL.md` and `DECISIONS.md` in this repository.
|
|
273
|
+
|
|
274
|
+
## What ships in the package
|
|
275
|
+
|
|
276
|
+
`npm install` ships only what `bskel` reads at runtime — `bin/`, `lib/`, `contracts/`,
|
|
277
|
+
`scanners/`, `handles/` (including every codegen template), `stack/` (including the catalog and
|
|
278
|
+
bootstrap templates), `schemas/`, and `scripts/preflight-base-ref.sh`. This repository's own test
|
|
279
|
+
suite, `SKILL.md` (this project's Claude Code skill definition), `DECISIONS.md`, and `CATALOG.md`
|
|
280
|
+
are not part of the published package — clone this repository directly if you want those.
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
MIT — see `LICENSE`.
|