feat/one-api
needs attentionviewing older commita2d2a54 · incrementalPR #326reviewed 2026-07-21 00:51 UTC2H · 7M · 3L · 3I- Purpose
- Establish the One-API program: encode the decision and canonical contract grammar as .claude/ rules (no docs/, no ADRs), plus W1 groundwork — machine-readable route-metadata inventory and merged public-id prefix registry. Classification only: no route behavior changes.
- Goal
- One API surface with visibility-as-metadata; a single contract tree from which OpenAPI, MCP tools, and the SDK generate; dogfood rule enforced.
- Sub-goals
- SG-1: .claude/rules/one-api.md is the SSOT for the One-API decision, wire grammar, operation grammar, and wave plan
- SG-2: canonical-form.md carries the SSOT-precedence rewording (Drizzle = shape truth; domain type = sole import surface)
- SG-3: Machine-readable route inventory classifies all 245 contract routes with proposed visibility/auth/scopes + dual-capability map
- SG-4: One PublicIdPrefix registry (shared-kernel) — utility-domain enum re-exports it; cfj kept; public schema regexes derive from constants
- SG-5: Typecheck/lint/test green after registry merge
- SG-6 (stretch): ADRs 016/018/020 distilled into owning rules; docs/ADRs/ retired
- What
- This incremental commit adds the 'Canonical contract form' section to one-api.md — the file-level target for contract layout, the TypeScript example of routeMeta() usage, and 6 rules that make the form canonical (RouteMeta shape, op mechanics, validation split, status declaration, handler typing, docs-as-route).
- Why
- Completes the W1 rule encoding by adding the prescriptive file layout that implementers will follow during W2+ convergence — the contract-layer twin of canonical-form.md.
- Areas
- .claude/rules+340−10.claude/skills+3−3.branch+1700−618scripts/one-api+5845−0packages/api+90−44packages/shared-kernel+74−22packages/database+5−3domains+6−32docs/ADRs+0−10256CLAUDE.md+2−2
- Blast
- Documentation-only in this commit. Whole branch: ~6.3k lines across rules, scripts (route-inventory, classify-routes), shared-kernel types, and API schemas. No runtime behavior changed. The route-inventory.json and shared-kernel id registry are the only consumed artifacts; all else is rules and reference data.
Findings · 15
correctness5
routeMeta() and RouteMeta do not exist yet — W2 deliverable presented in syntax-level positions
.claude/rules/one-api.md
The code snippet uses routeMeta({...}) and the prose describes RouteMeta as a closed typed shape with a builder in packages/api. Neither exists today. The rule acknowledges this is a W2 deliverable but the code sample treats it as callable syntax. Implementors may write contracts using routeMeta() before W2 lands and get TypeScript errors. The sample should be clearly marked as target/aspirational code.
withApiAuth does not exist — actual middleware is withPublicApiAuth (different name)
.claude/rules/one-api.md
The rule prescribes 'withApiAuth → validator → shell → mapper' in both the handler layout and RouteMeta prose. The actual codebase uses withPublicApiAuth exclusively — no withApiAuth symbol exists. The unified withApiAuth is also a W2 deliverable. The rule places withApiAuth in syntax-level positions without marking it as future, which will mislead developers trying to follow the pattern today.
docs-coverage-and-leak test exists and is correctly cited
apps/platform/src/lib/openapi/__tests__/docs-coverage-and-leak.test.ts
The test exists, covers one-doc-page-per-operation and an internal-term denylist. The citation in the rule is accurate.
publicIdRegex(PublicIdPrefix.X, 'i') signature is correct and in active use
packages/api/src/schemas/public/bill.public-schemas.ts
The prescribed form is confirmed in multiple public schema files. The rule's guidance is accurate.
Handler typing pattern typeof contract.x.handler is accurate
apps/platform/src/api/handlers/admin-organizations.handler.ts
This is the actual pattern used throughout the handler layer. Confirmed correct.
security2
Interim period has no type-enforced auth gate — safe-interim pattern not stated
.claude/rules/one-api.md
withApiAuth and routeMeta() don't exist until W2. Any new route written following this rule today has no auth enforcement. The rule should explicitly state the safe interim pattern (use existing withAuth/withPublicApiAuth wrapper) and require that no public-visibility routes are merged without it until W2 lands.
Router-level metadata default { capability } does not supply auth/scopes — absent fields must deny by default
.claude/rules/one-api.md:127
The example router default is { metadata: { capability: 'bills' } } — it sets only capability, not auth/scopes/visibility. The rule should state that missing auth or scopes on a route are a hard gate failure (deny by default, not allow-all). A route that accidentally omits routeMeta() inheriting only capability from the router default would otherwise become an unauthenticated endpoint.
conventions1
Target layout introduces validators/ directory not present in codebase or canonical-form.md
.claude/rules/one-api.md:88
The canonical layout shows validators/{capability}.validators.ts but the actual codebase uses apps/platform/src/api/utils/ (e.g. public-v1-validation.ts). canonical-form.md's API Surface Pattern section does not include a validators/ directory. As a prescriptive target this is intentional, but the rule should note the current utils/ home and migration path so implementers aren't confused about where existing validators live.
tests2
7+ public-v1 handlers use hardcoded prefix literals — rule violation with no enforcement test
apps/platform/src/api/handlers/public-v1/bills.handler.ts:29
The rule says 'ids validate via publicIdRegex(PublicIdPrefix.X, 'i'), never a prefix literal'. At least 7 handlers define hand-written literal regexes (/^bil_.../i, /^bfl_.../i, /^sit_.../i, /^cfj_.../i etc.). The public-id-prefix-registry.test.ts guards the registry but there is no test that fails when a handler defines a literal regex. A grep-based test scanning handlers/ for /^[a-z]{3}_/ string literals would mechanically enforce the rule.
route-inventory.json has no schema validation test — can silently go stale
scripts/one-api/route-inventory.json
The JSON file is the interim registry seed until W2 lands. There is no test that validates its structure (required fields, valid visibility/op/auth enum values, no duplicate routes). Without a schema assertion the file can drift from the shape the rule specifies and the W2 migration will be based on stale data.
improvement5
'operation grammar table (below)' is actually above the section
.claude/rules/one-api.md:139
The bullet reads 'Per-op mechanics come from the operation grammar table (below)' but the Operation grammar table sits at lines 68–79, which is above the newly inserted section. Change '(below)' to '(above)' or '(see § Operation grammar)'.
Invalid TypeScript in canonical example: responses: { 200: ..., 404: ... }
.claude/rules/one-api.md:122
The spread '...' inside an object value position is not valid TypeScript and cannot be copy-pasted. Either spell out the schema name (200: RecomputeResponseSchema) or use a comment placeholder — but not a bare '...' as a value.
Migration path from utils/public-v1-validation.ts to validators/ is unspecified
.claude/rules/one-api.md:88
The target layout introduces validators/{capability}.validators.ts but provides no guidance on when/how to split the monolithic public-v1-validation.ts. A developer converging a capability may leave the old file in place and end up with two overlapping gates. At minimum, add: 'During convergence, carve the capability's parse* functions from utils/public-v1-validation.ts into validators/{capability}.validators.ts; delete the origin once the twin is removed.'
No guidance on what happens to -internal contract files during convergence
.claude/rules/one-api.md:85
The section opens with 'One capability = ONE contract module, no -internal twin' but doesn't explain how existing -internal files should be treated. The DUAL convergence recipe says 'delete the twin' but doesn't name the file pattern. A developer touching bills finds bills.contract.ts AND bills-internal.contract.ts with no clear instruction on when/how to merge.
ts-rest version claim unverified — not pinned anywhere in the repo
.claude/rules/one-api.md:95
The rule states 'ts-rest ≥3.52 supports metadata per route AND per router' as a load-bearing architectural fact. No package.json peer constraint or comment points developers where to verify. Add a pointer: 'Verify with pnpm ls @ts-rest/core — must be ≥3.52 for router-level metadata merge.'
History · 47 commits
- 82bb5b9blockedincremental5H · 5M · 4L2026-08-12 01:48
- 90aa3d5needs attentionincremental1H · 5M · 3L2026-08-11 19:37
- 29d19a0needs attentionincremental1H · 5M · 9L2026-08-11 17:41
- 9bd8a0cneeds attentionfull0H · 5M · 9L2026-08-11 02:14
- 62ec3f7needs attentionincremental2H · 5M · 6L2026-08-10 22:51
- f93bca9needs attentionincremental2H · 5M · 8L2026-08-10 17:51
- 052db6fneeds attentionincremental1H · 3M · 4L2026-08-09 21:13
- 45699caneeds attentionincremental0H · 7M · 11L2026-08-09 17:44
- b843d8aneeds attentionincremental1H · 7M · 9L2026-08-09 04:05
- e1757b8needs attentionincremental0H · 3M · 6L2026-08-05 02:11
- 7a762faneeds attentionincremental2H · 5M · 5L2026-08-05 01:25
- 3300a60needs attentionincremental2H · 4M · 7L2026-08-04 19:06
- 0c8a7f5needs attentionincremental0H · 4M · 9L2026-08-04 18:15
- 345f42eneeds attentionincremental2H · 6M · 9L2026-08-04 17:28
- 8338a9aneeds attentionincremental5H · 14M · 14L2026-08-04 00:33
- 41be4c3needs attentionincremental0H · 5M · 7L2026-08-03 23:49
- 5ed593dneeds attentionincremental1H · 6M · 6L2026-08-03 21:32
- b333e25needs attentionincremental4H · 9M · 8L2026-08-03 21:00
- 5642cccneeds attentionincremental2H · 3M · 2L2026-08-03 20:17
- 73b0b39needs attentionincremental3H · 10M · 13L2026-07-31 18:29
- b19852eneeds attentionincremental0H · 1M · 5L2026-07-29 05:04
- 3845205needs attentionincremental3H · 6M · 4L2026-07-29 04:47
- eb8eb50needs attentionincremental0H · 1M · 2L2026-07-29 03:03
- f4720a3needs attentionincremental6H · 8M · 7L2026-07-29 02:54
- f8d341ablockedincremental2H · 2M · 5L2026-07-29 00:00
- a7f1a64needs attentionincremental2H · 8M · 8L2026-07-28 18:41
- 738b60bblockedincremental3H · 6M · 5L2026-07-28 00:46
- 2c248b6needs attentionincremental8H · 12M · 8L2026-07-27 23:23
- 1346cc0needs attentionincremental2H · 8M · 6L2026-07-27 20:15
- 0716018needs attentionincremental2H · 11M · 12L2026-07-27 19:22
- 215cd2dneeds attentionincremental3H · 6M · 5L2026-07-27 17:04
- ec46958needs attentionincremental0H · 3M · 5L2026-07-27 16:51
- de7b337blockedincremental4H · 9M · 14L2026-07-27 06:36
- b1bb9c0needs attentionincremental1H · 2M · 4L2026-07-27 05:09
- 4701d11needs attentionincremental0H · 4M · 3L2026-07-27 04:44
- e1626c4needs attentionincremental3H · 9M · 10L2026-07-27 03:21
- 195f198needs attentionincremental3H · 3M · 3L2026-07-25 01:22
- 42c7358safeincremental0H · 0M · 0L2026-07-22 20:46
- 85b9018needs attentionincremental0H · 1M · 6L2026-07-21 23:51
- a7b2a9aneeds attentionincremental0H · 9M · 12L2026-07-21 18:49
- c2ee0daneeds attentionincremental4H · 7M · 7L2026-07-21 02:17
- e8ffa5eneeds attentionincremental4H · 7M · 5L2026-07-21 01:33
- a2d2a54needs attentionincremental2H · 7M · 3L2026-07-21 00:51current
- 576fbd6needs attentionfull1H · 6M · 7L2026-07-21 00:35
- d3465e8needs attentionincremental1H · 7M · 10L2026-07-21 00:23
- dc794a7needs attentionincremental0H · 5M · 5L2026-07-20 23:46
- 9082773needs attentionfull1H · 3M · 3L2026-07-20 23:13