Skip to content

refactor: convert settings, skills, and RBAC routes to Hono sub-apps - #1729

Merged
ColeMurray merged 2 commits into
mainfrom
refactor/hono-pr6-modules
Sep 3, 2026
Merged

refactor: convert settings, skills, and RBAC routes to Hono sub-apps#1729
ColeMurray merged 2 commits into
mainfrom
refactor/hono-pr6-modules

Conversation

@ColeMurray

@ColeMurray ColeMurray commented Sep 3, 2026

Copy link
Copy Markdown
Owner

PR 6 of the Hono follow-up series (after #1724). Converts seven modules to Hono sub-apps, mounted at their existing catalog positions so precedence is unchanged:

Module Routes
model-provider-accounts 17
integration-settings 11
commit-signing 5
scm-settings 6
mcp-servers 5
skills 16
rbac 6

Every route reads admit(policy) followed by (c) => dispatch(c, handler). Handlers take the parameters Hono decoded, typed from the path literal, and each route's authentication, authorization, SCM support, and cache policy are what defineRoute(s) declared before.

Decode once. No handler in these modules calls decodeURIComponent any more.

  • integration-settings and scm-settings read :owner/:name through repositoryParams() (new routes/repository-params.ts), a validate-only check over the pair Hono already decoded. PR 4 adds the identical file for the repository family.
  • rbac drops decodePathSegment. Member ids keep the canonical-user-id check and its Invalid user ID answer, so the matrix case for a doubly-encoded member id still holds. The role route had no validity rule beyond decodability, and admission now refuses a segment Hono cannot decode on every route, so its Invalid role ID branch is gone rather than replaced by an invented pattern.
  • model-provider-accounts registers handlers directly instead of building Route objects through managementRoute(); the verify/disable/enable trio registers in a loop.

Tests.

  • scm-settings.test.ts dispatches through the production sub-app with a mocked store: a six-row wiring table asserts which store method each route reaches and that no other does, the storage-failure and malformed-settings cases are kept, and a new case shows web%2Fapp is refused after one decode while web%252Fapp reaches the store as web%2Fapp.
  • The policy test's malformed-role case is request-level: /roles/%E0%A4%A answers 400 Invalid path encoding before authentication, and D1 is never prepared.

Verification (on the tree rebased onto main at 7b561a5)

Check Result
Unit 234 files, 3,502 passed
Integration (workerd, real D1) 96 files, 1,129 passed
Route admission matrix + catalog conformance snapshots byte-identical
Typecheck (src + test), ESLint, Prettier clean

https://claude.ai/code/session_01KdDpTgGEjXpBA9SaGQVUH1

Summary by CodeRabbit

  • Bug Fixes

    • Malformed URL path encoding now returns a clear 400 Invalid path encoding response.
    • Repository paths containing encoded slashes are handled correctly without double-decoding.
    • Invalid repository path segments are rejected with a 400 response.
  • Improvements

    • Control-plane API routes now handle path parameters more consistently.
    • Required path parameters are validated consistently by the routing layer.
    • Repository path validation is applied consistently across supported endpoints.

Converts model-provider-accounts, integration-settings, commit-signing,
scm-settings, mcp-servers, skills, and rbac to Hono sub-apps mounted at
their catalog positions (66 routes). Each route is admit(policy) followed
by dispatch(c, handler); handlers take the parameters Hono decoded, typed
from the path literal, and the policies are unchanged.

Handlers stop decoding. integration-settings and scm-settings read the
repository pair through repositoryParams(), a validate-only check over
the segments Hono already decoded; rbac drops its decodePathSegment and
keeps the canonical-user-id check with its "Invalid user ID" answer.
Malformed percent-encoding is refused by admission on every route, so the
role route's own decode-failure branch is gone.

model-provider-accounts registers its handlers directly instead of
building routes through managementRoute(). The scm-settings tests
dispatch through the production sub-app with a mocked store and a
per-route wiring table; the policy test's malformed-role case is
request-level and shows D1 is never touched.

Claude-Session: https://claude.ai/code/session_01KdDpTgGEjXpBA9SaGQVUH1
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate
Tests

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 610633f5-5d1b-4488-a8cf-49726cdbdd91

📥 Commits

Reviewing files that changed from the base of the PR and between ce02392 and e7312b1.

📒 Files selected for processing (4)
  • packages/control-plane/src/routes/repository-params.ts
  • packages/shared/src/types/index.ts
  • packages/shared/src/types/repositories.ts
  • packages/shared/src/types/repository-contracts.test.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Control-plane route modules migrated from legacy route arrays to typed Hono routers with admit authorization and dispatch handlers. Repository path validation was centralized. Tests now exercise the full request pipeline and encoded-path behavior.

Changes

Control-plane routing

Layer / File(s) Summary
SCM and repository path handling
packages/control-plane/src/routes/scm-settings.ts, packages/control-plane/src/routes/integration-settings.ts, packages/control-plane/src/routes/repository-params.ts, packages/shared/src/types/repositories.ts, packages/shared/src/types/index.ts, packages/shared/src/types/repository-contracts.test.ts, packages/control-plane/src/routes/scm-settings.test.ts
SCM and integration settings routes now use typed Hono parameters, admission middleware, and dispatched handlers. Repository segment validation is shared and tested, including encoded slashes.
Account and commit-signing routes
packages/control-plane/src/routes/model-provider-accounts.ts, packages/control-plane/src/routes/commit-signing.ts
Model-provider account and commit-signing routes now use Hono routers and typed parameters. Existing methods, paths, permissions, and handler behavior remain registered through admit and dispatch.
MCP and RBAC routes
packages/control-plane/src/routes/mcp-servers.ts, packages/control-plane/src/routes/rbac.ts
MCP server and RBAC routes now use typed Hono parameters and shared admission policies. Legacy path-decoding and missing-parameter checks were removed.
Skills and profiles routes
packages/control-plane/src/routes/skills.ts
Skill and profile route arrays were replaced with a typed Hono router. Shared read, management, and profile admission policies register the endpoints, with preview routes ordered before parameterized routes.
Catalog mounting and request validation
packages/control-plane/src/routes/catalog.ts, packages/control-plane/src/router.policy.test.ts
Migrated routers are mounted as catalog entries. The malformed role-ID test now calls handleRequest and expects Invalid path encoding without a D1 query.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to e7312

Repository path validation is now centralized while preserving valid repository identifiers and rejecting malformed paths with the existing client-error behavior. The covered routing and validation changes show no remaining merge-blocking risk.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.96% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 67 functions across 14 files. 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 title accurately describes the Hono sub-app migration for settings, skills, and RBAC routes. It omits several additional migrated route modules, but it remains clear and related to the primary ref…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title accurately describes the Hono sub-app migration for settings, skills, and RBAC routes. It omits several additional migrated route modules, but it remains clear and related to the primary refactor.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/hono-pr6-modules

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.

@open-inspect open-inspect Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary

PR #1729, refactor: convert settings, skills, and RBAC routes to Hono sub-apps, by @ColeMurray converts seven control-plane route modules from legacy route arrays to admitted Hono sub-apps while preserving their catalog positions and request policies. The PR changes 11 files with 702 additions and 711 deletions; I found no blocking correctness, security, performance, or maintainability issues.

Critical Issues

None.

Suggestions

None.

Nitpicks

None.

Positive Feedback

  • Authentication, authorization, SCM support, cache directives, and route ordering remain consistent with the previous declarations.
  • The decode-once boundary is centralized in Hono admission, and repository pair validation preserves nested owner namespaces while rejecting decoded slashes in repository names.
  • The request-level SCM settings tests exercise production routing, admission, store wiring, malformed settings, storage failures, and single-decoding behavior.

Questions

None.

Verification

  • Control-plane unit suite: 234 files, 3,502 tests passed.
  • Focused routing suites: 3 files, 128 tests passed.
  • Control-plane TypeScript typecheck passed.
  • git diff --check passed.
  • GitHub CI checks are passing.

Verdict

Approve: Ready to merge.

@open-inspect open-inspect Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The Hono conversions preserve the existing route policies and catalog ordering, and they reduce the size of the converted modules. One maintainability blocker remains: repository-pair validity is now implemented independently in the route layer even though the shared repository identity module already owns the same invariant. This boundary is subtle (nested owners are valid; names containing decoded slashes are not), so two implementations are likely to drift as more route families migrate. Please factor validation of an already-decoded RepositoryPair into the shared identity module, have decodeRepositoryPathSegments compose it, and keep only HTTP error mapping in the route adapter. Add the decoded/nested-owner cases to the shared contract tests.

Verification: focused control-plane tests (116) passed; shared repository contract tests (35) passed; git diff --check passed; all GitHub CI checks are green.

owner: string;
name: string;
}): { owner: string; name: string } | Response {
const repository = parseRepositoryFullName(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[deep review] This duplicates the repository-pair validity algorithm already embedded in decodeRepositoryPathSegments (parse(format(pair)) plus the exact round-trip check), creating two canonical implementations of a subtle invariant. Nested owners make this especially risky: future rule changes can make legacy and Hono routes disagree. The code-judo move is to extract a shared validator for an already-decoded RepositoryPair, make decodeRepositoryPathSegments decode and delegate to it, and let this route adapter only rename fields/map null to the HTTP error. Please keep the nested-owner and slash-in-name cases in the shared contract tests so every transport gets the same semantics.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Done in e7312b1. validateRepositoryPathSegments(owner, name) in the shared identity module now owns the rule; decodeRepositoryPathSegments decodes and delegates, and the route adapter only maps null to the 400. The decoded and nested-owner cases are in the shared contract tests. The same commit is on #1727, which carries the identical file.

… module

Review follow-up. `repositoryParams()` re-implemented the pair
round-trip check that `decodeRepositoryPathSegments` embeds, leaving two
owners of a subtle rule (a nested owner is valid, a slash in a name is
not). `validateRepositoryPathSegments(owner, name)` now holds it once in
the shared package; the decoder decodes and delegates, and the route
adapter only maps null to the 400. The decoded and nested-owner cases
join the shared contract tests.

Claude-Session: https://claude.ai/code/session_01KdDpTgGEjXpBA9SaGQVUH1
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate
Tests

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@ColeMurray
ColeMurray merged commit bfd54a6 into main Sep 3, 2026
20 of 21 checks passed
@ColeMurray
ColeMurray deleted the refactor/hono-pr6-modules branch September 3, 2026 03:03
ColeMurray added a commit that referenced this pull request Sep 3, 2026
Last PR of the Hono series (#1720, #1721, #1723, #1724, #1726, #1727,
#1728, #1729). Every route module is a Hono sub-app, so the adapter that
carried catalog routes through admission has nothing left to serve.

## Removed

- `legacy()` and `legacyMatch()` in `routing/hono-app.ts`;
`createControlPlaneApp(modules, host)` mounts modules only.
- `RouteDefinition`, `Route`, `defineRoute()`, `defineRoutes()`, and
`extractRepoParams()` in `routes/shared.ts`. `RouteAdmissionPolicy` is
now an interface over `RoutePolicy` plus `authorization` and
`serviceActorClaims`; `cacheControl` lives on `AdmissionPolicy` in
`routing/admit.ts`.
- `RouteCatalogEntry`; the catalog is `readonly RouteModule[]`.
- `legacyRoutes()` in test support; `matchRoute()` no longer fabricates
a `RegExpMatchArray`.
- `routes/shared.test.ts`, replaced by
`routes/repository-params.test.ts` over the decoded pair.

## Kept on purpose

- `RouteParams`: admission's decoded-parameter dictionary. Hono exports
no such type.
- `rawRouteParams()`: the malformed-encoding guard's raw read-back
(added in #1724).

## Tests

Fixtures that need synthetic routes build a `Hono` module and pass it to
`createTestRequestHandler([module])`: the lifecycle suite, the contract
lister's suite, and the authorization-audit suite's eleven routes. The
two Worker-boundary suites build their shadow catalogs the same way:
conformance echoes the raw read-back through `rawRouteParams()`, and the
matrix's "raw path segments" probe from #1720 is now the decode-once
assertion on what handlers receive (`abc%2Fdef` arrives as `abc/def`,
`web%252Fapp` as `web%2Fapp`).

## Verification

| Check | Result |
|---|---|
| Typecheck (src, test, integration) | clean |
| ESLint, Prettier | clean |
| Unit | 234 files, 3,512 passed |
| Integration (workerd, real D1) | 96 files, 1,129 passed |
| Matrix and conformance snapshots | byte-identical |

https://claude.ai/code/session_01KdDpTgGEjXpBA9SaGQVUH1


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Bug Fixes**
- Repository paths now support nested owner namespaces, such as
`group/subgroup`.
- Repository names containing slash characters are rejected with a clear
validation message.
- URL path parameters are decoded consistently, improving handling of
encoded repository and member identifiers.

- **Refactor**
- Control-plane routing now uses the current route-module system,
providing consistent admission, route matching, and request handling
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

1 participant