← all branches

feat/one-api

needs attentionviewing older commit
576fbd6 · fullPR #326reviewed 2026-07-21 00:35 UTC1H · 6M · 7L · 8I
The branch
Purpose
Establish the One-API program's architectural foundation: encode the decision, wire grammar, and operation grammar as machine-readable .claude/ rules (not docs/ADRs), and provide the W1 groundwork every later parity wave builds on.
Goal
Classification-only groundwork: a machine-readable route-metadata inventory of all three API surfaces, one merged PublicIdPrefix registry, and ADR retirement. No route behavior changes.
Sub-goals
  • SG-1: .claude/rules/one-api.md as SSOT + canonical-form.md SSOT-precedence rewording
  • SG-2: scripts/one-api/classify-routes.ts + route-inventory.json (250 routes, dual-capability map)
  • SG-3: shared-kernel PublicIdPrefix registry (55 entities), re-export shims, cjb→cfj, publicIdRegex(), integrity test
  • SG-4: Retire docs/ADRs/ (23 files, −12k lines); distill 016/018/020 into owning rules
  • SG-N: Untrack .branch/ working files
The changes (whole branch)
What
New rule files (one-api.md, contracts-read-model.md), updated canonical-form.md and domain-patterns.md, merged PublicIdPrefix registry in shared-kernel with re-export shims across utility schema and api schemas, cjb→cfj rename in DB schema/tests/seeds, publicIdRegex() helper + 6-test integrity suite, route classifier script + emitted inventory JSON, and removal of 23 ADR files.
Why
The One-API program needs a single authoritative home for API design decisions and a machine-readable snapshot of the current route surface before W2 convergence work begins. ADRs were retired to reduce documentation drift.
Areas
.claude/rules/+2327packages/shared-kernel/src/+4512packages/api/src/+2814scripts/one-api/+58450domains/utility/src/+48packages/database/src/+64docs/ADRs/+07660
Blast
90 files, +6303/−13652 lines. Bulk is docs/ADRs/ deletion (−12k) and route-inventory.json (+5.4k generated). Substantive changes: ~260 lines of new/updated rules, ~60 lines of shared-kernel/schema/database changes. No runtime behavior changes.
cjb→cfj rename incomplete: 4 remaining stale references outside the PR's stated scope classifyOp POST heuristic distorts intent vs create counts by ~25 routes route-inventory.json checked in without staleness guard
typecheck· 52/52 packages — from PR descriptionlint· green on touched packages — from PR descriptionunit-tests· packages/api 303/303 incl. 6 new registry-integrity tests — from PR descriptionci· GitHub Actions status unavailable (token scope)coderabbit· No .coderabbit.yaml in repo

Findings · 23

correctness4

medium

services/ CFE Lambda still hardcodes cjb prefix

services/utility/bills/cfe/src/domain/constants.ts:205

PUBLIC_ID_PREFIXES.CFE_JOB = 'cjb' disagrees with the now-canonical 'cfj' in PublicIdPrefix.CfeJob. This is a second source of truth that wasn't updated. Any future Lambda code using it will produce the wrong prefix without a type error. Should import from @batu/shared-kernel instead.

medium

Seed file inserts rows with stale cjb_ prefix

packages/database/src/seed-utility-demo.ts:1352

Three demo job rows use hardcoded publicIds with the cjb_ prefix. These are unreachable by the now-generated cfj_ regex in PublicJobIdSchema. Update seed values to cfj_.

low

publicIdRegex i-flag accepts uppercase prefixes

packages/shared-kernel/src/types/ids.ts:94

The 'i' flag is intended to tolerate lowercase ULID body chars, but also makes the prefix match case-insensitive (e.g. 'BIL_...' would pass). No production code generates uppercase-prefix IDs today, but the contract is looser than stated.

low

classify-routes.ts misclassifies org-scoped creates as 'intent' (inventory data quality)

scripts/one-api/classify-routes.ts:215

Same root cause as the high finding above — the route-inventory.json W1 snapshot shows incorrect op counts as a result. Since this is 'ratify before W2', the inventory should be regenerated after the fix before wave planning.

security4

info

REVOKE wording: listing 'authenticated, anon, public' is redundant

.claude/rules/rls-checklist.md

Revoking from 'public' already covers all roles. The explicit enumeration is safe but could mislead a developer into thinking omitting 'public' is OK if authenticated+anon are listed.

info

publicIdRegex uses unescaped prefix in RegExp constructor

packages/shared-kernel/src/types/ids.ts:95

TypeScript type constrains prefix to [a-z]{3} registry values, so no real injection risk. A defensive escapeRegExp call would future-proof against misuse.

info

route-inventory.json reveals full internal route surface

scripts/one-api/route-inventory.json:1

Contains proposed auth classifications and all route paths. No secrets; paths are already in TypeScript contracts. Acceptable as a dev-tooling artifact.

info

isValidPublicId accepts any 3-char prefix (pre-existing)

packages/shared-kernel/src/types/ids.ts:124

Generic validator allows any 3-char prefix; entity-specific validation should use publicIdRegex(). Pre-existing behavior, not introduced here.

conventions5

medium

ontology.md and utility/CLAUDE.md still document cfe-job prefix as cjb_

.claude/rules/ontology.md:87

The entity table documents CfeJob prefix as `cjb_` — contradicts the new registry and will cause any developer/agent reading these auto-loaded rules to use the wrong prefix. Update both to `cfj_`.

medium

services/.../event-schemas.ts JSDoc still references cjb_

services/utility/bills/cfe/src/domain/event-schemas.ts:48

JSDoc `/** Job public ID (cjb_...) */` documents the retired prefix. Update to `cfj_`.

low

Handler/validation files still use hardcoded prefix regex literals

apps/platform/src/api/utils/asset-management-validation.ts:24

Multiple handler and validation files (asset-management-validation.ts, public-v1-validation.ts, bills.handler.ts, files.handler.ts, etc.) still use hardcoded regex literals instead of publicIdRegex(). The new one-api.md rule prohibits this. W1 scope is classification-only; these should be follow-up items.

low

cfe-job decision test fixtures use stale cjb_ prefix

domains/utility/src/cfe-job/__tests__/cfe-job.decisions.test.ts:56

Test fixtures use publicId: 'cjb_01HXYZ...' — decisions don't validate prefix format so no test failures, but teaches the wrong prefix to readers. Update to cfj_.

info

one-api.md Wave-1 single-mount statement could be misread as current state

.claude/rules/one-api.md:104

Wave-2 point #2 says 'one mount (/api/v1)' in present tense but this is a W4 target. Adding '(target: W4)' would prevent misreading.

tests3

low

publicIdRegex: no test for invalid Crockford Base32 characters

packages/api/src/__tests__/schemas/public-id-prefix-registry.test.ts:55

No assertion that characters outside the Crockford alphabet (I, L, O, U) are rejected. Low risk since generated ULIDs are always valid, but a rejection test would pin the character class exclusion.

low

publicIdRegex: no anchor smoke test

packages/api/src/__tests__/schemas/public-id-prefix-registry.test.ts:55

No test asserting that a padded string like 'prefix_bil_<ULID>suffix' is rejected. Anchors are present in the implementation; a test would guard against a future refactor removing them.

info

Re-export shims have no dedicated import smoke test — covered by type system

domains/utility/src/schema/base.ts

A wrong re-export would break at compile time. TypeScript coverage is sufficient.

improvement7

high

classifyOp misclassifies org-scoped POSTs as 'intent'

scripts/one-api/classify-routes.ts:1275

The condition `segments.some((s) => s.startsWith(':'))` treats any POST to a path containing ANY param as 'intent', even when the param is the org context (e.g. POST /api/organizations/:orgId/sites). True intent verbs live at the LAST segment. This misclassifies ~25 plain create routes as intent ops, distorting the W2/W3 wave scoping that depends on these counts. Fix: check only the last segment for intent-verb matching.

medium

route-inventory.json checked in with no staleness guard

scripts/one-api/route-inventory.json:1

Tracked generated file with a 'do not hand-edit' comment but no CI check that fails on drift and no gitignore rule. Will go stale with every contract change. Options: (a) gitignore + document run command in one-api.md, or (b) CI diff check.

medium

one-api.md rule active before W0 ratification checkpoint

.claude/rules/one-api.md:10

The rule encodes 'ratify with tech leads before Wave-2 work' but is already auto-loading for all api/** work. Agents/developers have no signal whether it's enforced or advisory. Add a one-liner: 'Enforcement: advisory until W0 ratification; Don't items are warnings until then'.

low

one-api.md 'Don't' rule deferred by 'once it exists' qualifier

.claude/rules/one-api.md:109

'Don't add a route outside the metadata registry (once it exists)' — the metadata registry lands in W2. Until then, agents don't know the interim behavior. Rephrase: 'Until W2, add every new route to route-inventory.json manually'.

low

HANDLER_SEGMENT_CAPABILITY allocated inside inner loop

scripts/one-api/classify-routes.ts:324

Two-key object literal re-allocated on every method iteration. Should be hoisted to module scope alongside APP_KEY_CAPABILITY and PUBLIC_SEGMENT_CAPABILITY for consistency and clarity.

info

classify-routes.ts has no tests; op classification logic is non-trivial

scripts/one-api/classify-routes.ts:1264

A few unit tests for classifyOp covering the intent-verb vs org-context-param cases would have caught the POST misclassification. Accepted as a script today, but worth adding if the classifier becomes the SSOT for W2+ tooling.

info

publicIdRegex creates new RegExp on every call — no memoization

packages/shared-kernel/src/types/ids.ts:94

Allocates a new RegExp per call. Called at module eval time across 6 schema files. Not a hot path, but a module-level Map cache would make repeated calls free and signal that these are stable, reusable patterns.

History · 47 commits

  1. 82bb5b9blockedincremental5H · 5M · 4L2026-08-12 01:48
  2. 90aa3d5needs attentionincremental1H · 5M · 3L2026-08-11 19:37
  3. 29d19a0needs attentionincremental1H · 5M · 9L2026-08-11 17:41
  4. 9bd8a0cneeds attentionfull0H · 5M · 9L2026-08-11 02:14
  5. 62ec3f7needs attentionincremental2H · 5M · 6L2026-08-10 22:51
  6. f93bca9needs attentionincremental2H · 5M · 8L2026-08-10 17:51
  7. 052db6fneeds attentionincremental1H · 3M · 4L2026-08-09 21:13
  8. 45699caneeds attentionincremental0H · 7M · 11L2026-08-09 17:44
  9. b843d8aneeds attentionincremental1H · 7M · 9L2026-08-09 04:05
  10. e1757b8needs attentionincremental0H · 3M · 6L2026-08-05 02:11
  11. 7a762faneeds attentionincremental2H · 5M · 5L2026-08-05 01:25
  12. 3300a60needs attentionincremental2H · 4M · 7L2026-08-04 19:06
  13. 0c8a7f5needs attentionincremental0H · 4M · 9L2026-08-04 18:15
  14. 345f42eneeds attentionincremental2H · 6M · 9L2026-08-04 17:28
  15. 8338a9aneeds attentionincremental5H · 14M · 14L2026-08-04 00:33
  16. 41be4c3needs attentionincremental0H · 5M · 7L2026-08-03 23:49
  17. 5ed593dneeds attentionincremental1H · 6M · 6L2026-08-03 21:32
  18. b333e25needs attentionincremental4H · 9M · 8L2026-08-03 21:00
  19. 5642cccneeds attentionincremental2H · 3M · 2L2026-08-03 20:17
  20. 73b0b39needs attentionincremental3H · 10M · 13L2026-07-31 18:29
  21. b19852eneeds attentionincremental0H · 1M · 5L2026-07-29 05:04
  22. 3845205needs attentionincremental3H · 6M · 4L2026-07-29 04:47
  23. eb8eb50needs attentionincremental0H · 1M · 2L2026-07-29 03:03
  24. f4720a3needs attentionincremental6H · 8M · 7L2026-07-29 02:54
  25. f8d341ablockedincremental2H · 2M · 5L2026-07-29 00:00
  26. a7f1a64needs attentionincremental2H · 8M · 8L2026-07-28 18:41
  27. 738b60bblockedincremental3H · 6M · 5L2026-07-28 00:46
  28. 2c248b6needs attentionincremental8H · 12M · 8L2026-07-27 23:23
  29. 1346cc0needs attentionincremental2H · 8M · 6L2026-07-27 20:15
  30. 0716018needs attentionincremental2H · 11M · 12L2026-07-27 19:22
  31. 215cd2dneeds attentionincremental3H · 6M · 5L2026-07-27 17:04
  32. ec46958needs attentionincremental0H · 3M · 5L2026-07-27 16:51
  33. de7b337blockedincremental4H · 9M · 14L2026-07-27 06:36
  34. b1bb9c0needs attentionincremental1H · 2M · 4L2026-07-27 05:09
  35. 4701d11needs attentionincremental0H · 4M · 3L2026-07-27 04:44
  36. e1626c4needs attentionincremental3H · 9M · 10L2026-07-27 03:21
  37. 195f198needs attentionincremental3H · 3M · 3L2026-07-25 01:22
  38. 42c7358safeincremental0H · 0M · 0L2026-07-22 20:46
  39. 85b9018needs attentionincremental0H · 1M · 6L2026-07-21 23:51
  40. a7b2a9aneeds attentionincremental0H · 9M · 12L2026-07-21 18:49
  41. c2ee0daneeds attentionincremental4H · 7M · 7L2026-07-21 02:17
  42. e8ffa5eneeds attentionincremental4H · 7M · 5L2026-07-21 01:33
  43. a2d2a54needs attentionincremental2H · 7M · 3L2026-07-21 00:51
  44. 576fbd6needs attentionfull1H · 6M · 7L2026-07-21 00:35current
  45. d3465e8needs attentionincremental1H · 7M · 10L2026-07-21 00:23
  46. dc794a7needs attentionincremental0H · 5M · 5L2026-07-20 23:46
  47. 9082773needs attentionfull1H · 3M · 3L2026-07-20 23:13