Skip to content

refactor: extract the CLI-independent describe seam and scaffold mcp/sdk#9

Merged
kikashy merged 2 commits into
mainfrom
feat/seam-prep
Jul 24, 2026
Merged

refactor: extract the CLI-independent describe seam and scaffold mcp/sdk#9
kikashy merged 2 commits into
mainfrom
feat/seam-prep

Conversation

@kikashy

@kikashy kikashy commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Pure refactor, no behavior change. Prepares the seam the MCP skeleton needs.

Why

The composition of the version and spec schema payloads lived inside cobra RunE closures (app.go:124-131, :279-290). The only way to obtain either was to invoke the CLI, so a second surface would have to import the cobra-carrying cli package or rebuild the payloads itself — and a rebuilt payload is a second contract that drifts from the first.

What

New internal/describe, holding no CLI, transport, or presentation concerns:

func Runtime(set *artifacts.Set, command string) result.Version
func Schema(set *artifacts.Set, specVersion, command string, schemaBytes []byte) (result.Schema, error)

Error mapping stays in the CLI, where the distinction between an unsupported specification version (ExitUnsupported / JPS-CAPABILITY-SPEC-VERSION) and a corrupt bundled artifact (ExitInternal / JPS-ARTIFACT-SCHEMA) is already expressed. Collapsing those into one error return would have lost a distinction the CLI contract makes.

WrittenTo deliberately does not move — it records what a caller did, not what the artifact is. A test asserts the package never populates it.

Also removes stringFrom, now dead in cli, and the three imports it was the last user of.

The gate for a pure refactor

Captured every output in both formats before and after:

output result
version --format json identical
version (human) identical
spec schema 0.1.0-draft --format json identical
spec schema 0.1.0-draft (human) identical
spec validate - --format json identical
spec test-conformance --format json identical

Byte-identical, via cmp, not eyeballed.

Contract pinning

internal/result had no test file at all. The machine contract — outputVersion, tool.name, the six exit classes, and the four envelope members every payload carries — was enforced only by convention, so a rename or an accidental version bump passed silently. Now pinned by literal, with the reasoning in a comment so the next person knows the failure is the point.

The envelope test marshals to JSON and asserts on the emitted members rather than the struct fields, since a json: tag rename is invisible to the type checker but breaks every consumer.

Verified

gofmt -l empty · go vet clean · 11 packages ok (was 10) · check-release --tag v0.1.0 verified · conformance 47/47 exit 0

Next

internal/mcp calls these two functions plus validation.Engine.Validate and conformance.Runner.Run — all four now reachable without cobra.

🤖 Generated with Claude Code

kikashy and others added 2 commits July 23, 2026 18:35
…dent seam

The composition of the `version` and `spec schema` payloads lived inside cobra
`RunE` closures, so the only way to obtain either was to invoke the CLI. Any
second surface would have had to either import the cobra-carrying `cli`
package or rebuild the payloads itself, and a rebuilt payload is a second
contract that drifts.

Move both into `internal/describe`, which holds no CLI, transport, or
presentation concerns. Error mapping stays in the CLI, where the distinction
between an unsupported specification version and a corrupt bundled artifact is
already expressed as distinct exit classes and diagnostic codes.

`WrittenTo` deliberately does not move: it records what a caller did, not what
the artifact is, and a test asserts the package never populates it.

Pure refactor. Verified by capturing `version`, `spec schema`,
`spec validate`, and `spec test-conformance` output in both human and JSON
form before and after: all six are byte-identical.

Also pins the machine contract in tests -- `outputVersion`, `tool.name`, the
six exit classes, and the four envelope members every payload carries. These
were enforced only by convention, so a rename or an accidental version bump
passed silently. `internal/result` had no test file at all before this.

Removes `stringFrom`, now dead in `cli`, and three imports it was the last
user of.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Brian Jin <35789537+kikashy@users.noreply.github.com>
Reserve two package homes as documented, empty Go packages so the planned
adapter and composition seams have obvious locations before any code lands:

- internal/mcp: a transport adapter that will expose this runtime over the
  Model Context Protocol as an "mcp" subcommand of the single binary,
  delegating to the existing core and adding no judgment behavior.
- sdk: a public, experimental in-process Go composition API for embedding the
  runtime without invoking the binary.

Each holds only a doc.go carrying a package clause and an intent comment, so
both build clean and add no behavior. No public interface changes: internal/mcp
is unimportable outside this module, and sdk exports nothing and is marked
pre-1.0 unstable. Other-language clients are out of scope here by design; they
reach the runtime over a wire protocol or the binary, not by importing sdk.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Brian Jin <35789537+kikashy@users.noreply.github.com>
@kikashy kikashy changed the title Extract runtime and schema description behind a CLI-independent seam refactor: extract the CLI-independent describe seam and scaffold mcp/sdk Jul 24, 2026
@kikashy

kikashy commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Added ddfa5c3: scaffolds the empty internal/mcp and sdk package seams this PR's describe extraction was preparing — doc.go only, no behavior, internal/mcp unimportable outside the module and sdk exporting nothing. The feat/seam-prep branch is now complete.

@kikashy
kikashy merged commit a7de345 into main Jul 24, 2026
4 checks passed
@kikashy
kikashy deleted the feat/seam-prep branch July 24, 2026 22:07
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