Skip to content

refactor(surface,cli): Zod-Core-Out vertical slice for surface #114

@stackbilt-admin

Description

@stackbilt-admin

Context

@stackbilt/surface extracts HTTP routes (Hono, Express, itty-router) and D1 SQL schema from a project's source. It powers the charter surface CLI command and is a prerequisite for the #113 repo-brief proposal.

Today, surface's API contracts are plain TypeScript interfaces (Route, SchemaTable, SchemaColumn). No runtime validation, no machine-readable schema. charter serve currently exposes zero surface-related MCP tools — agents can't invoke route/schema extraction through MCP.

This is the same shape @stackbilt/blast had before #110 — and the same Core-Out refactor resolves it.

Proposal: Zod-Core-Out, applied to surface

Exactly the pattern proven in #110:

  • SurfaceInputSchema + SurfaceOutputSchema become the authoritative contracts in @stackbilt/surface.
  • High-level analyze(input: SurfaceInput): SurfaceOutput composes the existing extractRoutes + extractSchema primitives.
  • charter surface CLI routes argv → schema → analyze → human formatter.
  • charter serve registers charter_surface as a callable MCP tool.

Scope

@stackbilt/surface (additive):

  • Add SurfaceInputSchema and SurfaceOutputSchema.
  • Add analyze(input: SurfaceInput): SurfaceOutput as the adapter entry point.
  • Re-export existing Route, SchemaTable, SchemaColumn interfaces as z.infer<> aliases.
  • Add runtime zod dependency (^3.24.1), matching the blast decision. Update "Zero dependencies" README claim.

@stackbilt/cli (additive):

  • commands/surface.ts — route argv through SurfaceInputSchema.parse. ZodError → CLIError mapping, same shape as blast's CLI adapter.
  • commands/serve.ts — register charter_surface tool following the charter_blast pattern (raw shape advertised, authoritative validation in the handler via SurfaceInputSchema.parse).

Decisions locked upfront (per #110 review)

  1. Zod → JSON-schema path: pass the Zod raw shape directly to server.registerTool. No new library.
  2. Schema location: @stackbilt/surface. Zod becomes a runtime dep — same rationale as blast: programmatic consumers need the authoritative schema from the package surface, not from a downstream adapter.
  3. Snapshot tests: structural assertions via SurfaceOutputSchema.parse on representative fixtures. No byte-identical snapshots.
  4. Defaults as shared constants: any default input values (e.g. default include/exclude globs) live as exported constants referenced by both the schema's .default() and the primitive's in-function fallback. Same DEFAULT_MAX_DEPTH pattern used in blast.

Acceptance criteria

  • @stackbilt/surface exports SurfaceInputSchema, SurfaceOutputSchema, SurfaceInput, SurfaceOutput, analyze.
  • Existing extractSurface, extractRoutes, extractSchema, formatSurfaceMarkdown exports preserved (OSS additive-only policy).
  • charter surface <root> CLI output is structurally equivalent to current behavior.
  • charter serve registers charter_surface; tools/list surfaces the JSON schema with per-field .describe() strings.
  • SurfaceOutputSchema.parse(analyze(...)) succeeds on a representative fixture with both routes and D1 tables.
  • packages/surface/package.json lists zod as a dependency, not devDependency.
  • Unit tests cover schema validation + analyze via synthetic fixtures. No child-process tests.
  • No removal or rename of existing public API.

Why this is a prerequisite for #113

#113 (repo-brief) composes blast + surface + config-file reads into a pre-digested brief. Blast's Core-Out shipped in 0.11.0 (#110); surface's is outstanding. Consuming un-Core-Out'd surface output in the brief would cause the brief's Surface section to reshape with each future refactor — exactly the drift the Core-Out pattern was designed to prevent.

Out of scope

  • Breaking changes to Route / SchemaTable / SchemaColumn shapes. Re-exported as z.infer<> aliases; no consumer breaks.
  • Additional framework support (Next.js routes, tRPC, etc.). Separate issue.
  • Removing the existing (server.registerTool as Function) casts in serve.ts. Follow-up once the SDK ships cleaner typings (flagged in refactor(blast,cli,serve): Zod-Core-Out vertical slice (#109) #110 already).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttype:refactorCode restructuring without behavior change

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions