Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
581 lines (467 loc) · 19.9 KB
/
Copy pathMakefile
File metadata and controls
581 lines (467 loc) · 19.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
# Fizzy SDK Makefile
.PHONY: all check clean help
# Default: run all checks
all: check
#---
# Smithy targets
#---
.PHONY: smithy-validate smithy-build smithy-check smithy-clean smithy-mapper
smithy-mapper:
@echo "==> Building Smithy mapper plugin..."
cd spec/smithy-bare-arrays && ./gradlew publishToMavenLocal --quiet
smithy-validate:
@echo "==> Validating Smithy model..."
cd spec && smithy validate
smithy-build: smithy-mapper ## Build OpenAPI from Smithy
@echo "==> Building OpenAPI spec..."
cd spec && smithy build
@cp spec/build/smithy/openapi/openapi/Fizzy.openapi.json openapi.json
@echo "==> OpenAPI spec generated: openapi.json"
@$(MAKE) sync-api-version
@$(MAKE) behavior-model
smithy-check: smithy-validate smithy-mapper
@echo "==> Checking OpenAPI freshness..."
@cd spec && smithy build
@TMPFILE=$$(mktemp) && \
cp spec/build/smithy/openapi/openapi/Fizzy.openapi.json "$$TMPFILE" && \
(diff -q openapi.json "$$TMPFILE" > /dev/null 2>&1 || \
(rm -f "$$TMPFILE" && echo "ERROR: openapi.json is out of date. Run 'make smithy-build'" && exit 1)) && \
rm -f "$$TMPFILE"
@echo " openapi.json is fresh"
smithy-clean:
rm -rf spec/build spec/smithy-bare-arrays/build spec/smithy-bare-arrays/.gradle
#---
# Behavior Model
#---
.PHONY: behavior-model behavior-model-check
behavior-model:
@echo "==> Generating behavior model..."
./scripts/generate-behavior-model
@echo " behavior-model.json generated"
behavior-model-check:
@echo "==> Checking behavior model freshness..."
@./scripts/generate-behavior-model --check
#---
# URL Routes
#---
.PHONY: url-routes url-routes-check
url-routes:
@echo "==> Generating URL routes..."
./scripts/generate-url-routes
url-routes-check:
@echo "==> Checking URL routes freshness..."
@./scripts/generate-url-routes --check
#---
# API Version Sync
#---
.PHONY: sync-api-version sync-api-version-check
sync-api-version:
@echo "==> Syncing API version..."
./scripts/sync-api-version.sh
sync-api-version-check:
@echo "==> Checking API version sync..."
@API_VER=$$(jq -r '.info.version' openapi.json); \
ok=true; \
grep -q "APIVersion = \"$$API_VER\"" go/pkg/fizzy/version.go || ok=false; \
grep -q "API_VERSION = \"$$API_VER\"" typescript/src/client.ts || ok=false; \
grep -q "API_VERSION = \"$$API_VER\"" ruby/lib/fizzy/version.rb || ok=false; \
grep -q "API_VERSION = \"$$API_VER\"" kotlin/sdk/src/commonMain/kotlin/com/basecamp/fizzy/FizzyConfig.kt || ok=false; \
grep -q "apiVersion = \"$$API_VER\"" swift/Sources/Fizzy/FizzyConfig.swift || ok=false; \
grep -q "API_VERSION: &str = \"$$API_VER\"" rust/fizzy-sdk/src/generated/mod.rs || ok=false; \
if [ "$$ok" = false ]; then echo "ERROR: API_VERSION constants out of sync. Run 'make sync-api-version'"; exit 1; fi
@echo " API version is in sync"
#---
# Provenance
#---
.PHONY: provenance-sync provenance-check sync-status
provenance-sync:
@echo "==> Syncing provenance..."
@cp spec/api-provenance.json go/pkg/fizzy/api-provenance.json
@echo " Provenance synced"
provenance-check:
@echo "==> Checking provenance..."
@diff -q spec/api-provenance.json go/pkg/fizzy/api-provenance.json > /dev/null 2>&1 || \
(echo "ERROR: go/pkg/fizzy/api-provenance.json is out of date. Run 'make provenance-sync'" && exit 1)
@echo " Provenance is in sync"
sync-status:
@REV=$$(jq -r '.fizzy.revision // empty' spec/api-provenance.json); \
if [ -z "$$REV" ]; then \
echo "==> fizzy: no baseline revision set"; \
exit 0; \
fi; \
command -v gh > /dev/null 2>&1 || { echo "ERROR: gh CLI not found. Install: https://cli.github.com"; exit 1; }; \
gh auth status > /dev/null 2>&1 || { echo "ERROR: gh not authenticated. Run: gh auth login"; exit 1; }; \
echo "==> Upstream Fizzy API-related changes since $$(echo $$REV | cut -c1-7):"; \
gh api "repos/basecamp/fizzy/compare/$$REV...main" \
--jq '[.files[] | select((.filename == "docs/api/README.md") or (.filename | startswith("docs/api/sections/")) or (.filename == "config/routes.rb") or (.filename | startswith("app/controllers/")) or (.filename | startswith("app/views/")) or (.filename | startswith("app/models/")))] | if length == 0 then " (no API-doc/app-surface changes detected)" else .[] | " " + .status[:1] + " " + .filename end'
#---
# Go SDK (delegates to go/Makefile)
#---
.PHONY: go-generate-services go-test go-lint go-check go-clean go-check-drift
go-generate-services:
@echo "==> Generating Go services..."
cd go && go run ./cmd/generate-services/
go-test:
@$(MAKE) -C go test
go-lint:
@$(MAKE) -C go lint
go-check:
@$(MAKE) -C go check
go-clean:
@$(MAKE) -C go clean
go-check-drift:
@echo "==> Checking Go service drift..."
./scripts/check-service-drift.sh
#---
# TypeScript SDK
#---
.PHONY: ts-install ts-generate ts-generate-services ts-build ts-test ts-typecheck ts-check ts-check-drift ts-clean
TS_NODE_STAMP := typescript/node_modules/.install-stamp
$(TS_NODE_STAMP): typescript/package-lock.json typescript/package.json
@echo "==> Installing TypeScript dependencies..."
cd typescript && npm ci
@touch $(TS_NODE_STAMP)
ts-install: $(TS_NODE_STAMP)
ts-generate: ts-install
ts-generate-services: ts-install
ts-build: ts-install
ts-test: ts-install
ts-typecheck: ts-install
ts-generate:
@echo "==> Generating TypeScript types..."
cd typescript && npm run generate
ts-generate-services:
@echo "==> Generating TypeScript services..."
cd typescript && npx tsx scripts/generate-services.ts
ts-build:
@echo "==> Building TypeScript SDK..."
cd typescript && npm run build
ts-test:
@echo "==> Running TypeScript tests..."
cd typescript && npm test
ts-typecheck:
@echo "==> Type-checking TypeScript..."
cd typescript && npm run typecheck
ts-check-drift:
@echo "==> Checking TypeScript service drift..."
./scripts/check-ts-service-drift.sh
ts-check: ts-typecheck ts-test
@echo "==> TypeScript SDK checks passed"
ts-clean:
rm -rf typescript/dist typescript/node_modules
#---
# Ruby SDK
#---
.PHONY: rb-generate rb-generate-services rb-build rb-test rb-check rb-check-drift rb-doc rb-clean
rb-generate:
@echo "==> Generating Ruby SDK types and metadata..."
cd ruby && ruby scripts/generate-metadata.rb > lib/fizzy/generated/metadata.json
cd ruby && ruby scripts/generate-types.rb
rb-generate-services:
@echo "==> Generating Ruby services..."
cd ruby && ruby scripts/generate-services.rb
rb-build:
@echo "==> Building Ruby SDK..."
cd ruby && bundle install
rb-test:
@echo "==> Running Ruby tests..."
cd ruby && bundle exec rake test
rb-check: rb-test
@echo "==> Running Ruby linter..."
cd ruby && bundle exec rubocop
@echo "==> Ruby SDK checks passed"
rb-check-drift:
@echo "==> Checking Ruby service drift..."
./scripts/check-rb-service-drift.sh
rb-doc:
@echo "==> Generating Ruby documentation..."
cd ruby && bundle exec rake doc
@echo "Documentation generated in ruby/doc/"
rb-clean:
rm -rf ruby/.bundle ruby/vendor ruby/doc ruby/coverage
#---
# Swift SDK (delegates to swift/Makefile)
#---
.PHONY: swift-build swift-test swift-check swift-check-drift swift-generate swift-clean
swift-build:
@$(MAKE) -C swift build
swift-test:
@$(MAKE) -C swift test
swift-check:
@$(MAKE) -C swift check
swift-generate:
@$(MAKE) -C swift generate
swift-clean:
@$(MAKE) -C swift clean
swift-check-drift:
@echo "==> Checking Swift service drift..."
./scripts/check-swift-service-drift.sh
#---
# Kotlin SDK
#---
.PHONY: kt-generate-services kt-build kt-test kt-check kt-check-drift kt-clean
kt-generate-services:
@echo "==> Generating Kotlin services..."
cd kotlin && ./gradlew :generator:run --args="--openapi ../openapi.json --behavior ../behavior-model.json --output sdk/src/commonMain/kotlin/com/basecamp/fizzy/generated"
kt-build:
@echo "==> Building Kotlin SDK..."
cd kotlin && ./gradlew :fizzy-sdk:build
kt-test:
@echo "==> Running Kotlin tests..."
cd kotlin && ./gradlew :fizzy-sdk:check
kt-check: kt-test
@echo "==> Kotlin SDK checks passed"
kt-check-drift:
@echo "==> Checking Kotlin service drift..."
./scripts/check-kotlin-service-drift.sh
kt-clean:
cd kotlin && ./gradlew clean
#---
# Rust SDK (delegates to rust/Makefile)
#---
.PHONY: rs-generate rs-generate-services rs-build rs-test rs-test-lib rs-test-features rs-lint rs-doc rs-deny rs-check rs-check-drift rs-publish-check rs-clean
rs-generate:
@echo "==> Generating Rust types, routes and services..."
@$(MAKE) -C rust generate
rs-generate-services: rs-generate
rs-build:
@$(MAKE) -C rust build
rs-test:
@$(MAKE) -C rust test
rs-test-lib:
@$(MAKE) -C rust test-lib
rs-test-features:
@$(MAKE) -C rust test-features
rs-lint:
@$(MAKE) -C rust lint
rs-doc:
@$(MAKE) -C rust doc
rs-deny:
@$(MAKE) -C rust deny
rs-check-drift:
@echo "==> Checking Rust service drift..."
@$(MAKE) -C rust generate-check
./scripts/check-rs-service-drift.sh
rs-publish-check:
@$(MAKE) -C rust publish-check
rs-check:
@$(MAKE) -C rust check
@echo "==> Rust SDK checks passed"
rs-clean:
@$(MAKE) -C rust clean
#---
# Conformance
#---
.PHONY: conformance-build conformance-go conformance-kotlin conformance-typescript conformance-ruby conformance-swift conformance-runner-tests-swift conformance-rust conformance-rs conformance-runner-tests-rust conformance
conformance-build:
@echo "==> Building conformance runners..."
cd conformance/runner/go && go build -o conformance-runner .
cd conformance/runner/typescript && npm ci
cd kotlin && ./gradlew :conformance:build
cd conformance/runner/rust && cargo build --release --locked
cd conformance/runner/swift && swift build
conformance-go: conformance-build
@echo "==> Running Go conformance..."
cd conformance/runner/go && ./conformance-runner ../../tests/
conformance-typescript: ts-build
@echo "==> Running TypeScript conformance..."
cd conformance/runner/typescript && npm ci && npm test
conformance-ruby:
@echo "==> Running Ruby conformance..."
cd conformance/runner/ruby && bundle install --quiet && ruby runner.rb ../../tests/
conformance-kotlin:
@echo "==> Running Kotlin conformance..."
cd kotlin && ./gradlew :conformance:run
conformance-swift: conformance-runner-tests-swift
@echo "==> Running Swift conformance..."
cd conformance/runner/swift && swift run ConformanceRunner ../../tests/
conformance-runner-tests-swift:
@echo "==> Running Swift conformance runner tests..."
cd conformance/runner/swift && swift test
conformance-rust: conformance-runner-tests-rust
@echo "==> Running Rust conformance..."
cd conformance/runner/rust && cargo run -q --release --locked
conformance-rs: conformance-rust
conformance-runner-tests-rust:
@echo "==> Running Rust conformance runner tests..."
cd conformance/runner/rust && cargo test --locked
conformance: conformance-go conformance-typescript conformance-ruby conformance-swift conformance-kotlin conformance-rust
@echo "==> All conformance tests passed"
#---
# Version & Release
#---
.PHONY: bump release audit-check
bump:
ifndef VERSION
$(error VERSION is required. Usage: make bump VERSION=x.y.z)
endif
@echo "==> Bumping version to $(VERSION)..."
./scripts/bump-version.sh $(VERSION)
@echo " Version bumped to $(VERSION)"
release:
ifndef VERSION
$(error VERSION is required. Usage: make release VERSION=x.y.z)
endif
@echo "==> Releasing v$(VERSION)..."
@[ "$$(git rev-parse --abbrev-ref HEAD)" = "main" ] || \
{ echo "ERROR: Must be on main branch to release."; exit 1; }
@grep -qF 'Version = "$(VERSION)"' go/pkg/fizzy/version.go || \
{ echo "ERROR: Go version does not match. Run 'make bump VERSION=$(VERSION)' first."; exit 1; }
@grep -qF '"version": "$(VERSION)"' typescript/package.json || \
{ echo "ERROR: TypeScript version does not match."; exit 1; }
@grep -qF 'VERSION = "$(VERSION)"' ruby/lib/fizzy/version.rb || \
{ echo "ERROR: Ruby version does not match."; exit 1; }
@grep -qF 'sdkVersion = "$(VERSION)"' swift/Sources/Fizzy/FizzyConfig.swift || \
{ echo "ERROR: Swift version does not match."; exit 1; }
@grep -qF 'VERSION = "$(VERSION)"' kotlin/sdk/src/commonMain/kotlin/com/basecamp/fizzy/FizzyConfig.kt || \
{ echo "ERROR: Kotlin version does not match."; exit 1; }
@grep -qF 'version = "$(VERSION)"' kotlin/sdk/build.gradle.kts || \
{ echo "ERROR: Kotlin Gradle project version does not match."; exit 1; }
@grep -qF 'VERSION = "$(VERSION)"' typescript/src/client.ts || \
{ echo "ERROR: TypeScript client.ts version does not match."; exit 1; }
@RS_VERSION=$$(cargo metadata --no-deps --format-version 1 --manifest-path rust/Cargo.toml | \
jq -r '.packages[] | select(.name == "fizzy-sdk") | .version'); \
[ "$$RS_VERSION" = "$(VERSION)" ] || \
{ echo "ERROR: Rust crate version ($$RS_VERSION) does not match."; exit 1; }
@cargo metadata --locked --format-version 1 --manifest-path rust/Cargo.toml > /dev/null || \
{ echo "ERROR: rust/Cargo.lock is stale. Run 'make bump VERSION=$(VERSION)' first."; exit 1; }
@cargo metadata --locked --format-version 1 --manifest-path conformance/runner/rust/Cargo.toml > /dev/null || \
{ echo "ERROR: conformance/runner/rust/Cargo.lock is stale. Run 'make bump VERSION=$(VERSION)' first."; exit 1; }
@git diff --quiet && git diff --cached --quiet && \
test -z "$$(git status --porcelain)" || \
{ echo "ERROR: Working tree has uncommitted or untracked changes."; git status --short; exit 1; }
$(MAKE) check
git tag "v$(VERSION)"
git push origin "v$(VERSION)"
@echo " Released v$(VERSION)"
audit-check:
@echo "==> Checking rubric audit..."
@test -f rubric-audit.json || (echo "ERROR: rubric-audit.json not found" && exit 1)
@for c in 1A.6 1B.2 1C.3; do \
pass=$$(jq -r --arg c "$$c" '.criteria[$$c].pass // empty' rubric-audit.json); \
if [ "$$pass" != "true" ]; then \
echo "ERROR: Must-pass criterion $$c is not passing in rubric-audit.json" && exit 1; \
fi; \
done
@audit_date=$$(jq -r '.date' rubric-audit.json); \
days_old=$$(( ( $$(date +%s) - $$(date -j -f '%Y-%m-%d' "$$audit_date" +%s 2>/dev/null || date -d "$$audit_date" +%s) ) / 86400 )); \
if [ "$$days_old" -gt 30 ]; then \
echo "ERROR: rubric-audit.json is $$days_old days old (max 30)" && exit 1; \
fi
@echo " rubric-audit.json is fresh and must-pass criteria verified"
#---
# Combined
#---
.PHONY: check-mvp check-full
check-mvp: smithy-check behavior-model-check url-routes-check sync-api-version-check go-check
@echo "==> MVP checks passed"
check-full: check-mvp provenance-check audit-check ts-check rb-check swift-check kt-check rs-check conformance
@echo "==> Full checks passed"
check: smithy-check behavior-model-check url-routes-check sync-api-version-check provenance-check audit-check go-check go-check-drift ts-check ts-check-drift rb-check rb-check-drift swift-check swift-check-drift kt-check kt-check-drift rs-check rs-check-drift conformance
@echo "==> All checks passed"
clean: smithy-clean go-clean ts-clean rb-clean swift-clean kt-clean rs-clean
@echo "==> Cleaned"
help:
@echo "Fizzy SDK Makefile"
@echo ""
@echo "Smithy:"
@echo " smithy-validate Validate Smithy spec syntax"
@echo " smithy-mapper Build custom OpenAPI mapper JAR"
@echo " smithy-build Build OpenAPI from Smithy (updates openapi.json)"
@echo " smithy-check Verify openapi.json is up to date"
@echo " smithy-clean Remove Smithy build artifacts"
@echo ""
@echo "Behavior Model:"
@echo " behavior-model Generate behavior-model.json from Smithy spec"
@echo " behavior-model-check Verify behavior-model.json is up to date"
@echo ""
@echo "URL Routes:"
@echo " url-routes Generate url-routes.json from OpenAPI spec"
@echo " url-routes-check Verify url-routes.json is up to date"
@echo ""
@echo "Go SDK:"
@echo " go-generate-services Generate service files from openapi.json"
@echo " go-test Run Go tests"
@echo " go-lint Run Go linter"
@echo " go-check Run all Go checks"
@echo " go-check-drift Check service layer drift vs generated client"
@echo " go-clean Remove Go build artifacts"
@echo ""
@echo "TypeScript SDK:"
@echo " ts-generate Generate types and metadata from OpenAPI"
@echo " ts-generate-services Generate service classes from OpenAPI"
@echo " ts-build Build TypeScript SDK"
@echo " ts-test Run TypeScript tests"
@echo " ts-typecheck Run TypeScript type checking"
@echo " ts-check Run all TypeScript checks"
@echo " ts-clean Remove TypeScript build artifacts"
@echo ""
@echo "Ruby SDK:"
@echo " rb-generate Generate types and metadata from OpenAPI"
@echo " rb-generate-services Generate service classes from OpenAPI"
@echo " rb-build Build Ruby SDK (install deps)"
@echo " rb-test Run Ruby tests"
@echo " rb-check Run all Ruby checks (test + rubocop)"
@echo " rb-doc Generate YARD documentation"
@echo " rb-clean Remove Ruby build artifacts"
@echo ""
@echo "Swift SDK:"
@echo " swift-generate Generate service classes from OpenAPI"
@echo " swift-build Build Swift SDK"
@echo " swift-test Run Swift tests"
@echo " swift-check Run all Swift checks"
@echo " swift-clean Remove Swift build artifacts"
@echo ""
@echo "Kotlin SDK:"
@echo " kt-generate-services Generate service classes from OpenAPI"
@echo " kt-build Build Kotlin SDK"
@echo " kt-test Run Kotlin tests"
@echo " kt-check Run all Kotlin checks"
@echo " kt-check-drift Check service drift vs OpenAPI spec"
@echo " kt-clean Remove Kotlin build artifacts"
@echo ""
@echo "Rust SDK:"
@echo " rs-generate Generate types, routes and services from openapi.json"
@echo " rs-generate-services Alias for rs-generate (the fleet vocabulary)"
@echo " rs-build Build the Rust workspace"
@echo " rs-test Run Rust tests (SDK, generator, conformance runner)"
@echo " rs-test-lib Run the SDK tests alone (the MSRV leg)"
@echo " rs-test-features Test with no default features; build the examples"
@echo " rs-lint Run rustfmt and clippy"
@echo " rs-doc Build the crate docs"
@echo " rs-deny Run cargo deny (advisories, licenses, bans, sources)"
@echo " rs-check Run all Rust checks"
@echo " rs-check-drift Check generated code and route drift vs OpenAPI"
@echo " rs-publish-check Run cargo publish --dry-run"
@echo " rs-clean Remove Rust build artifacts"
@echo ""
@echo "Conformance:"
@echo " conformance Run all conformance tests"
@echo " conformance-go Run Go conformance tests"
@echo " conformance-kotlin Run Kotlin conformance tests"
@echo " conformance-typescript Run TypeScript conformance tests"
@echo " conformance-ruby Run Ruby conformance tests"
@echo " conformance-swift Run Swift conformance tests"
@echo " conformance-runner-tests-swift Run the Swift conformance runner's own tests"
@echo " conformance-rust Run Rust conformance tests"
@echo " conformance-runner-tests-rust Run the Rust conformance runner's own tests"
@echo " conformance-build Build conformance test runners"
@echo ""
@echo "Provenance:"
@echo " provenance-sync Copy provenance into Go package for go:embed"
@echo " provenance-check Verify Go embedded provenance is up to date"
@echo " sync-status Show upstream changes since last spec sync"
@echo ""
@echo "Version & Release:"
@echo " bump VERSION=x.y.z Bump SDK version across all languages"
@echo " sync-api-version Sync API_VERSION from openapi.json"
@echo " sync-api-version-check Verify API_VERSION constants are up to date"
@echo " release VERSION=x.y.z Tag and push a release (triggers all SDK releases)"
@echo " audit-check Validate rubric-audit.json freshness and criteria"
@echo ""
@echo "Combined:"
@echo " check-mvp Fast MVP checks (smithy + behavior + routes + api-version + go)"
@echo " check-full Full CI-grade checks (check-mvp + all languages + conformance)"
@echo " check Run all checks"
@echo " clean Remove all build artifacts"
@echo " help Show this help"