Skip to content

fix: enforce staged strict REST contract gates - #1051

Merged
kang-heewon merged 2 commits into
trunkfrom
fix/981-strict-rest-contract-gate
Jun 20, 2026
Merged

fix: enforce staged strict REST contract gates#1051
kang-heewon merged 2 commits into
trunkfrom
fix/981-strict-rest-contract-gate

Conversation

@kang-heewon

@kang-heewon kang-heewon commented Jun 20, 2026

Copy link
Copy Markdown
Member

Fixes #981.

Summary

  • Adds a staged strict contract typecheck gate for @croco/protocols-core with exact optional properties, unchecked indexed access, and index-signature property access checks, then wires it into pnpm check and generated-app smoke preflight.
  • Adds static and oxlint REST generated-template gates for @ALL, schema-less @Body(), and schema-less named @Param/@Query/@Header decorators.
  • Updates SaaS and AI SaaS generated templates so REST routes declare schema-backed decorators, and documents the new gates with patch changesets.

Verification

  • pnpm exec vitest run scripts/tests/static-misuse-check.spec.ts scripts/tests/strict-contract-typecheck.spec.ts scripts/tests/package-quality-report.spec.ts - passed, 16 tests.
  • pnpm test --filter=@croco/oxlint-rules - passed, 5 tests.
  • pnpm exec oxlint packages/create-croco-app/templates - passed, 126 files.
  • pnpm static-misuse:check - passed.
  • pnpm strict-contract-typecheck - passed.
  • pnpm check - passed.
  • pnpm create-croco-app:smoke - passed; all generated app smoke cases passed, including the new strict/static/oxlint preflight and SaaS/AI SaaS contract flows.
  • pnpm typecheck - passed, 214/214 Turbo tasks.
  • pnpm test --filter=@croco/protocols-core - passed.
  • pnpm typecheck --filter=@croco/protocols-core - passed.
  • pnpm test --filter=@croco/protocols-rest - passed.
  • git diff --check and git diff --cached --check - passed.
  • pnpm changeset-required:check -- --base origin/trunk --head HEAD - passed.
  • Pre-commit hook passed: staged oxlint and oxfmt.
  • Pre-push hook passed: full pnpm test 215/215 Turbo tasks and full pnpm typecheck 214/214 Turbo tasks.

Self-review gates

  • Correctness/regression: PASS. The issue acceptance criteria are covered by a strict protocols-core rollout config with an empty baseline, fatal/unclassified TypeScript output handling, static and AST lint fixtures for forbidden generated REST patterns, template schema fixes, pnpm check integration, and generated-app smoke preflight plus downstream contract/RPC/OpenAPI smoke flows.
  • API/security/compatibility/release: PASS. No public protocols-core exports changed; the oxlint rule is additive and disabled by default outside generated templates; compatibility-mode app code remains outside the generated-template gate; no dependencies or lockfile changes were introduced; patch changesets cover @croco/protocols-core and create-croco-app.
  • Maintainability/minimality: PASS. The diff is scoped to the staged strict gate, generated REST misuse checks, affected templates, docs, tests, smoke wiring, and release metadata. The line-oriented static checker stays scoped to templates while the import-aware oxlint rule covers the full generated template TypeScript scope.

Independent review

A read-only adversarial review initially found three issues: generated-app smoke did not run the new gates, oxlint only covered controller paths, and the strict typecheck parser could pass fatal unparsed compiler output. The final patch adds smoke preflight gates, broadens the import-aware oxlint override to all generated template TypeScript files, and treats global/unclassified TypeScript output as fatal with regression coverage.

Risk

Low-medium. The strict gate currently filters diagnostics to @croco/protocols-core so transitive dependency strict failures do not block this rollout stage; additional packages should be added explicitly with their own strict-clean state or reviewed baselines.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 생성된 REST API 라우트에서 스키마 기반 계약 검증이 기본으로 적용됩니다. 헤더, 경로 매개변수, 쿼리 매개변수 및 요청 본문에 대한 자동 스키마 검증이 강화되었습니다.
    • 더 엄격한 TypeScript 타입 체킹이 단계적으로 도입되어 코드 안정성이 향상됩니다.
  • 문서

    • REST 계약 스키마 요구사항 및 엄격한 타입 검사 프로세스 설명서가 추가되었습니다.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kang-heewon, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 31 minutes and 25 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: be7c586f-719a-45c8-ab90-bdfe7ed7d8c8

📥 Commits

Reviewing files that changed from the base of the PR and between 26a0af0 and 6de4892.

📒 Files selected for processing (9)
  • packages/create-croco-app/templates/ai-saas/apps/api-server/src/controllers/AiController.ts
  • packages/create-croco-app/templates/ai-saas/apps/api-server/src/controllers/aiSchemas.ts
  • packages/create-croco-app/templates/saas/apps/api-server/src/controllers/JobsController.ts
  • packages/create-croco-app/templates/saas/apps/api-server/src/controllers/schemas.ts
  • packages/oxlint-rules/src/tests/rest-generated-contract-schema.spec.ts
  • scripts/static-misuse-check.mts
  • scripts/strict-contract-typecheck.mts
  • scripts/tests/static-misuse-check.spec.ts
  • scripts/tests/strict-contract-typecheck.spec.ts
📝 Walkthrough

Walkthrough

@croco/protocols-core를 대상으로 staged strict TypeScript 타입체크 게이트(strict-contract-typecheck)를 도입하고, 생성된 REST 앱 템플릿에 스키마 기반 계약 데코레이터 강제를 위한 rest-generated-contract-schema ESLint 규칙과 static misuse 체크 규칙을 추가한다. 관련 템플릿 컨트롤러·스키마가 새 규칙에 맞게 업데이트되고 smoke 스크립트 및 pnpm check에 게이트가 연결된다.

Changes

Staged Strict Contract Typecheck & REST Misuse Gate

Layer / File(s) Summary
strict-contract-typecheck 타입 및 구현
scripts/strict-contract-typecheck.mts
StrictContractDiagnostic 등 공개 타입 정의, 진단 정규화·수집(collectStrictContractDiagnostics)·비교(compareStrictContractDiagnostics) 함수, tsc 실행 및 baseline diff CLI 진입점 전체 구현.
protocols-core strict tsconfig 및 baseline
packages/protocols-core/tsconfig.contract-strict.json, tsconfig/contract-strict.baseline.json
exactOptionalPropertyTypes, noUncheckedIndexedAccess, noPropertyAccessFromIndexSignature를 활성화하는 protocols-core 전용 strict tsconfig와 비어 있는 초기 baseline JSON 추가.
protocols-core strict 옵션 대응 코드 수정
packages/protocols-core/src/libs/ContractGraph.ts, packages/protocols-core/src/libs/ContractGraphSnapshot.ts
strict tsconfig 적용에 맞춰 getContractPathParams()flatMap 기반으로 변경하고, isContractGraphSnapshot()의 속성 접근을 대괄호 표기로 변경.
rest-generated-contract-schema ESLint 규칙 구현
packages/oxlint-rules/src/rules/rest-generated-contract-schema.ts, packages/oxlint-rules/src/index.ts
@All 사용, 스키마 없는 @Body(), 스키마 없는 named param 데코레이터를 보고하는 신규 RuleModule 구현 및 플러그인 레지스트리 등록.
static-misuse-check에 REST 계약 규칙 추가
scripts/static-misuse-check.mts
rest-generated-contract-schema-boundary StaticMisuseRule 추가, matchSchemaLessNamedParamDecorator 헬퍼로 named param 스키마 누락 패턴 감지, STATIC_MISUSE_RULES를 두 규칙으로 확장.
oxlintrc override 및 패키지 스크립트 연결
.oxlintrc.json, package.json
templates/**/*.ts에 대해 rest-generated-contract-schemaerror로 활성화하는 override 추가, strict-contract-typecheck 스크립트 정의.
생성 템플릿 스키마 및 데코레이터 업데이트
packages/create-croco-app/templates/ai-saas/.../AiController.ts, packages/create-croco-app/templates/ai-saas/.../aiSchemas.ts, packages/create-croco-app/templates/saas/.../JobsController.ts, packages/create-croco-app/templates/saas/.../schemas.ts
AiController의 @HeaderoptionalTenantIdHeaderSchema 적용, JobsController의 @Param/@QueryjobIdSchema 및 optional 쿼리 스키마 적용, 관련 Zod 스키마 상수 추가.
smoke 스크립트에 계약 게이트 연결
scripts/create-croco-app-generated-smoke.mts, scripts/package-quality-report.mts
smoke 실행 전 runGeneratedAppContractGates()로 strict-contract-typecheck·static-misuse:check·oxlint 3단계 게이트를 순차 실행, quality report Evidence 문구 업데이트.
테스트 및 문서
scripts/tests/strict-contract-typecheck.spec.ts, packages/oxlint-rules/src/tests/rest-generated-contract-schema.spec.ts, scripts/tests/static-misuse-check.spec.ts, scripts/tests/package-quality-report.spec.ts, packages/oxlint-rules/src/tests/index.spec.ts, docs/static-misuse-checks.md, docs/strict-contract-typecheck.md, .changeset/strict-rest-contract-gate.md
신규 유틸·규칙·static-misuse 동작 검증 테스트 추가, Generated REST Contract Rule 문서 및 strict-contract-typecheck 동작 원리 문서 추가.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(70, 130, 180, 0.5)
    Note over CICheck,baseline: pnpm check / smoke 실행
  end
  participant CICheck as pnpm check / smoke
  participant runGeneratedAppContractGates
  participant strictTypecheck as strict-contract-typecheck
  participant staticMisuse as static-misuse:check
  participant oxlint as oxlint (templates)
  participant tsc as tsc (protocols-core)
  participant baseline as contract-strict.baseline.json

  CICheck->>runGeneratedAppContractGates: 게이트 순차 실행
  runGeneratedAppContractGates->>strictTypecheck: pnpm strict-contract-typecheck
  strictTypecheck->>tsc: spawnSync tsc --project tsconfig.contract-strict.json
  tsc-->>strictTypecheck: 진단 출력
  strictTypecheck->>baseline: readBaseline()
  baseline-->>strictTypecheck: 기준 진단 목록
  strictTypecheck-->>runGeneratedAppContractGates: passed / exit(1)
  runGeneratedAppContractGates->>staticMisuse: pnpm static-misuse:check
  staticMisuse-->>runGeneratedAppContractGates: passed / exit(1)
  runGeneratedAppContractGates->>oxlint: pnpm exec oxlint packages/create-croco-app/templates
  oxlint-->>runGeneratedAppContractGates: passed / exit(1)
  runGeneratedAppContractGates-->>CICheck: 모든 게이트 통과
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • croco-dev/framework#815: packages/protocols-core/src/libs/ContractGraph.ts의 경로 파라미터 파싱 파이프라인을 도입한 PR로, 이번 PR에서 해당 파일의 getContractPathParams() 로직을 strict 옵션 대응을 위해 변경함.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix: enforce staged strict REST contract gates' accurately summarizes the main objective: implementing phased strict TypeScript and REST contract validation gates. The title clearly conveys the primary change focus.
Linked Issues check ✅ Passed The PR implementation comprehensively addresses all coding requirements from issue #981: staged strict TypeScript config for protocols-core [#981], REST misuse rules preventing @All and enforcing schemas [#981], integration into pnpm check and smoke tests [#981], and package-level allowlist with tsconfig.contract-strict.json [#981].
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #981 scope: strict TS gates, REST contract rules, template updates, and integration points. No changes appear unrelated to the staged strict contract enforcement objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/981-strict-rest-contract-gate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown

📊 Benchmark Results

❌ Some benchmarks failed

Gate failures

  • CrocoApp constructor: p75 6.9μs exceeds baseline 3.4μs by more than 20%
  • CrocoApp lambdaHandler (10 controllers): p75 179.7μs exceeds baseline 33.3μs by more than 20%
  • Lambda cold-start simulation: p75 337.6μs exceeds baseline 70.2μs by more than 20%
  • Lambda cold-start with headers: p75 308.2μs exceeds baseline 66.7μs by more than 20%
  • Lambda cold-start with binary body: p75 266.9μs exceeds baseline 63.2μs by more than 20%
  • Lambda cold-start with query params: p75 236.4μs exceeds baseline 63.9μs by more than 20%
  • Lambda cold-start with authorizer context: p75 238.8μs exceeds baseline 59.8μs by more than 20%
  • Lambda cold-start realistic scenario: p75 235.3μs exceeds baseline 60.2μs by more than 20%
  • EventBusConfig.start (10 handlers): p75 1.4μs exceeds baseline 0.9μs by more than 20%
  • EventPublisher.publishNow single event: p75 1.6μs exceeds baseline 1.1μs by more than 20%
  • DefaultHandlerResolver.resolve × 10: p75 0.1μs exceeds baseline 0.0μs by more than 20%
  • Container.get singleton (cold): p75 73.6μs exceeds baseline 0.6μs by more than 20%
  • Container.register × 50 components: p75 3.2ms exceeds baseline 12.1μs by more than 20%
  • Container.validate (50 components): p75 3.3ms exceeds baseline 29.7μs by more than 20%
  • Container.get singleton (warm): p75 1.6μs exceeds baseline 0.3μs by more than 20%
  • lambdaPreset config creation: p75 1.5μs exceeds baseline 1.0μs by more than 20%
Benchmark p75 Threshold Baseline vs Baseline Status Notes
CrocoApp constructor 6.9μs 30.0ms 3.4μs +103.6% -
CrocoApp lambdaHandler (10 controllers) 179.7μs 50.0ms 33.3μs +439.7% -
Lambda cold-start simulation 337.6μs 80.0ms 70.2μs +381.1% -
Lambda cold-start with headers 308.2μs 80.0ms 66.7μs +361.7% -
Lambda cold-start with binary body 266.9μs 80.0ms 63.2μs +322.6% -
Lambda cold-start with query params 236.4μs 80.0ms 63.9μs +270.0% -
Lambda cold-start with authorizer context 238.8μs 80.0ms 59.8μs +299.3% -
Lambda cold-start realistic scenario 235.3μs 80.0ms 60.2μs +291.1% -
EventBusConfig.start (10 handlers) 1.4μs 10.0ms 0.9μs +59.2% -
EventPublisher.publishNow single event 1.6μs 2.0ms 1.1μs +51.7% -
DefaultHandlerResolver.resolve × 10 0.1μs 5.0ms 0.0μs +90.5% -
Container.get singleton (cold) 73.6μs 5.0ms 0.6μs +11683.7% -
Container.register × 50 components 3.2ms 10.0ms 12.1μs +26226.4% -
Container.validate (50 components) 3.3ms 20.0ms 29.7μs +10989.2% -
Container.get singleton (warm) 1.6μs 500.0μs 0.3μs +455.8% -
TelemetryRuntime.init (lambda preset) 1.1ms 200.0ms 69.3ms -98.4% -
lambdaPreset config creation 1.5μs 2.0ms 1.0μs +45.2% -

Updated: 2026-06-20T18:16:24.460Z · Commit: 676598b

@kang-heewon

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/create-croco-app/templates/ai-saas/apps/api-server/src/controllers/aiSchemas.ts`:
- Line 3: The constant optionalTenantIdHeaderSchema uses camelCase naming
convention but the repository requires SCREAMING_SNAKE_CASE for all constant
declarations in TypeScript and JavaScript files. Rename
optionalTenantIdHeaderSchema to OPTIONAL_TENANT_ID_HEADER_SCHEMA in the
declaration within the aiSchemas.ts file, and update all import statements and
usages of this constant throughout the codebase to use the new
SCREAMING_SNAKE_CASE name.

In
`@packages/create-croco-app/templates/saas/apps/api-server/src/controllers/schemas.ts`:
- Around line 173-176: Rename the four exported schema constants in the
schemas.ts file from camelCase to SCREAMING_SNAKE_CASE: jobIdSchema should
become JOB_ID_SCHEMA, optionalJobStatusQuerySchema should become
OPTIONAL_JOB_STATUS_QUERY_SCHEMA, optionalJobTypeQuerySchema should become
OPTIONAL_JOB_TYPE_QUERY_SCHEMA, and optionalJobsIntegerQuerySchema should become
OPTIONAL_JOBS_INTEGER_QUERY_SCHEMA. After renaming these constants in their
declaration locations, also update all corresponding import statements and
usages of these constants in JobsController.ts to reference the new
SCREAMING_SNAKE_CASE names.

In `@packages/oxlint-rules/src/tests/rest-generated-contract-schema.spec.ts`:
- Around line 56-58: The test file is throwing a generic Error which violates
the repository exception policy that restricts generic Error throws. In the test
helper function around the importListener type check, replace the throw new
Error statement with a Vitest assertion instead. Use Vitest's expect API to
assert that importListener is a function type, which will fail the test
appropriately if the condition is not met, rather than throwing a generic Error
exception.

In `@scripts/static-misuse-check.mts`:
- Around line 106-121: The regex patterns in the match functions at lines 106,
112, and in the matchSchemaLessNamedParamDecorator function are matching
decorator patterns anywhere in the line, including within comments. To fix this,
modify the match functions to exclude commented portions of the line before
applying the regex. Either strip everything after // in each line before
matching, or add a check to skip lines that only contain decorator usage within
comments. This will prevent false positives when decorators are mentioned in
documentation comments or example comments.

In `@scripts/strict-contract-typecheck.mts`:
- Around line 166-213: The current implementation in the main function reads the
baseline and compares only the diagnostics, but does not validate that the
baseline metadata (version, strictOptions, packages) matches the current
configuration. After reading the baseline using readBaseline(rootDir) and before
calling compareStrictContractDiagnostics, add validation checks to ensure the
baseline version, strictOptions, and packages list align with the current
rollout configuration tracked by rolloutPackages. If any metadata mismatch is
detected, throw an error to prevent the gate from passing with stale or drifted
contract data.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2867dbc1-325c-4e1c-8b8a-280c076a02fb

📥 Commits

Reviewing files that changed from the base of the PR and between 955b02e and 26a0af0.

📒 Files selected for processing (24)
  • .changeset/strict-rest-contract-gate.md
  • .oxlintrc.json
  • docs/static-misuse-checks.md
  • docs/strict-contract-typecheck.md
  • package.json
  • packages/create-croco-app/templates/ai-saas/apps/api-server/src/controllers/AiController.ts
  • packages/create-croco-app/templates/ai-saas/apps/api-server/src/controllers/aiSchemas.ts
  • packages/create-croco-app/templates/saas/apps/api-server/src/controllers/JobsController.ts
  • packages/create-croco-app/templates/saas/apps/api-server/src/controllers/schemas.ts
  • packages/oxlint-rules/src/index.ts
  • packages/oxlint-rules/src/rules/rest-generated-contract-schema.ts
  • packages/oxlint-rules/src/tests/index.spec.ts
  • packages/oxlint-rules/src/tests/rest-generated-contract-schema.spec.ts
  • packages/protocols-core/src/libs/ContractGraph.ts
  • packages/protocols-core/src/libs/ContractGraphSnapshot.ts
  • packages/protocols-core/tsconfig.contract-strict.json
  • scripts/create-croco-app-generated-smoke.mts
  • scripts/package-quality-report.mts
  • scripts/static-misuse-check.mts
  • scripts/strict-contract-typecheck.mts
  • scripts/tests/package-quality-report.spec.ts
  • scripts/tests/static-misuse-check.spec.ts
  • scripts/tests/strict-contract-typecheck.spec.ts
  • tsconfig/contract-strict.baseline.json

Comment thread scripts/static-misuse-check.mts
Comment thread scripts/strict-contract-typecheck.mts
@kang-heewon
kang-heewon merged commit f8e4056 into trunk Jun 20, 2026
8 checks passed
@kang-heewon
kang-heewon deleted the fix/981-strict-rest-contract-gate branch June 20, 2026 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[typecheck] contract/core 패키지의 strict TS·REST misuse gate를 단계 적용한다

1 participant