Agent-first toolchain: CLI, guest SDK and a home template - #10
Conversation
Adds packages/cli, the agent-first command surface for building, checking, publishing and rolling back one home component. Git stays the user's: the tool never commits, never bundles and never invents a control-plane endpoint. - closed RFC 0004 §13.2 client: capability, delivery, finalization, activation, with the two reconciliation reads a lost response needs; - publisher-side pre-check of the RFC 0002 §7.2 artifact rules, so module syntax, a dynamic import, a source map or an oversized program fails before an upload capability is spent; - one stable exit code and one stable failure kind per outcome, plus --json, so an agent decides without parsing prose; - the Home Key is read from MIAKAPP_HOME_KEY only, never from an argument. --expected-generation is required rather than discovered: RFC 0004 §13.2 publishes no read for the current component pointer, and asking for the number is better than guessing one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds packages/component, the typed guest half of the ABI 1 bridge. Shapes mirror component-runtime/src/runtime-broker.ts rather than a reading of the RFC: the broker is the authority, and a disagreement is a bug here. - handshake, coalesced render loop and lifecycle, with commits spaced to the thirty-per-second ABI rate so a burst cannot terminate the instance; - typed builders for the twelve semantic node types, with closed token enums and no way to emit a key or an undefined value the bridge would reject; - state projection that exposes staleness instead of pretending cached values are current, per RFC 0002 §12.2; - call correlation that keeps an unknown outcome distinct from a failure, so a component cannot retry a call the home may already have applied; - local refusal of the operations the broker answers by terminating: publishing or calling while staged, suspended or inactive. packages/component/examples/salon.ts bundles to a 11 KB classic Worker program that miakapp check accepts, which exercises both packages end to end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A coordinator that owns the logic, a component that is the interface, and a project file that ties them to a control plane. It drives one lamp and reports one temperature; both are meant to be replaced. The coordinator configuration is a pure function of its options, so the authorization that matters is tested without a relay, a control plane or a network: a non-owner is refused, a coordinator-originated call is refused, a malformed argument is refused before the hardware is touched, and a hardware failure is not reported as success. One test exists for a failure that is otherwise silent. A component receives the intersection of what it requests and what the coordinator grants, so a requirement in miakapp.yaml that stateAccess does not cover is not a publication error — the path simply never arrives and the interface renders a hole. The correspondence is asserted instead. The Miakapp dependencies use file: paths so the template stays verified inside the repository; the README gives the three commands that replace them. Wired as check:template, which rebuilds the SDK, the CLI and the guest SDK, reinstalls from source and runs typecheck, bundle, tests and the offline artifact check. Also fixes a real bug this template found. canonicalHttpsUrl demanded byte-for-byte URL round-tripping, and `URL` re-serializes a bare origin with a trailing slash. Every origin-only control plane was therefore rejected, including `https://control.example.test`, which is RFC 0004 §3's own example issuer. An empty path is now accepted in either spelling and nothing else is relaxed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI was red on `templates/home/test/home.test.ts`: "Cannot find package 'miakapi'". The template is not a workspace member — it resolves `miakapi` and `@miakapp/cli` through `file:` links that exist only after its own install — so the root sweep could never run it. It reached it anyway because `bun test foo/` is a substring filter, not a directory scope, and `test/` also selects `templates/home/test/`. On Bun 1.4 the test then resolved `miakapi` by self-reference and passed; on the 1.2.23 CI pins it did not. Two halves, because either alone leaves the template unproven: - the root sweep names its files as globs, so it runs exactly what the root install resolves and nothing from templates/; - CI runs `check:packages`, which installs the template from scratch and runs its build, tests and `@miakapp/cli check`. That script already existed and nothing called it — the same rot the template was written to avoid. `test/check-coverage.test.ts` pins both. It expands the sweep globs, walks the tree for test files and manifests, follows the script chain, and reads the workflow, so it fails when a test file escapes every check CI runs rather than when someone forgets to update a list. Six negative controls, each verified red then reverted: substring filters restored; a sweep directory dropped; `check:packages` removed from the workflow; the template dropped from `check:packages`; a new unlinked project with tests; a test file in a subdirectory the globs miss. Verified on Bun 1.2.23 with no template install, as CI runs it: `bun run check` 216/216, `bun run check:packages` 31 + 38 + 8 green and the template artifact publishable.
`examples/salon.ts` imports the package by name, the way a consumer writes it, so its types resolve through `exports` to `dist/index.d.ts`. The check ran `typecheck` first, before `build` had produced that file, so from a clean tree the example could not compile: TS2307, then two implicit `any` on the callbacks it could no longer type. Nothing caught it because nothing ran the script. It passed by hand only when a `dist/` from an earlier build was still lying around. Now that CI runs `check:packages` on a fresh checkout, the order has to be real: build, then typecheck, then test. The example keeps importing by package name rather than a relative path into `src`, which is the point — it compiles against the surface consumers get. Verified on Bun 1.2.23 from a clean tree (no `dist/` anywhere, no template install): `bun run check` 216/216, `bun run check:packages` green through cli, component and the template.
…ockfile Running `check:packages` on a fresh checkout exposed two more ways the template was never really exercised. `bunx @miakapp/cli check` went to the registry and got a 404. The package is `private: true`, so it exists nowhere to fetch — and the template already declares it as a devDependency, which links `node_modules/.bin/miakapp`. The scripts now call that binary, so they run the CLI this checkout built rather than whatever a registry might one day answer. The READMEs keep `bunx` for the first `init`, where there is no project to install into yet. `templates/home/bun.lock` declared lockfileVersion 2, written by a newer Bun than the 1.2.23 this repository pins. CI could not parse it: "Unknown lockfile version", "Ignoring lockfile", then a fresh resolve and "Saved lockfile". A lockfile that the pinned toolchain silently discards is worse than none, since it still looks like a pin. Regenerated with 1.2.23 as version 1, which both 1.2.23 and 1.4 read, and `check:template` now installs `--frozen-lockfile` so drift fails loudly instead of being rewritten in passing. Three assertions added to test/check-coverage.test.ts, each verified red then reverted: a template script fetching a private package through bunx; a lockfile whose format differs from the root's; an install in a CI-run script that is not frozen. Nine controls total on that file now. Verified on Bun 1.2.23, clean tree: `bun run check` 219/219, `bun run check:packages` green through cli, component and the template, with no lockfile warning.
Green — and the template is now actually exercisedThis PR was red on
Nine negative controls, each verified red then reverted: substring filters restored · a sweep directory dropped · Two of the six original controls passed green on the first attempt, exactly as on #179 — the discovery had missed Verified, on Bun 1.2.23 installed separately to match the pin, from a clean tree with no |
|
Note issue d'une répétition de la porte de sortie de l'atelier F jouée ce matin sur l'union locale de Un point concerne directement le bun remove miakapi @miakapp/component @miakapp/cli
bun add miakapi @miakapp/component
bun add -d @miakapp/cliVérifié sur le registre :
Les deux 404 sont bruyants, donc inoffensifs. Le troisième ne l'est pas : Je ne touche à rien : la correction dépend de la décision de publication (quels noms, quel tag, quand), et elle n'est pas de mon ressort. Mais tant que les paquets ne sont pas publiés, cette section du README promet un chemin qui n'existe pas. |
Three packages that let a coding agent build, check and publish a home component without being a Miakapp developer.
@miakapp/cliBuilds, checks, publishes and rolls back. Git stays the user's — the tool never commits, never bundles and never invents a control-plane endpoint.
--json, so an agent decides without parsing prose. Exit 7 isunknown_outcome: the effect may already have committed, so the next step is a read, never a blind retry.MIAKAPP_HOME_KEYonly, never from an argument. A test asserts it appears on neither stdout nor stderr.--expected-generationis required rather than discovered, because RFC 0004 §13.2 publishes no read for the current component pointer. Asking for the number beat inventing a route.@miakapp/componentThe guest half of the ABI 1 bridge. Shapes mirror
component-runtime/src/runtime-broker.tsrather than a reading of RFC 0002 — the broker is the authority, and a disagreement is a bug in this package.undefinedvalue the bridge would reject.templates/homeA working home: coordinator, component, project file, 8 tests. The coordinator configuration is a pure function of its options, so the authorization that matters is tested without a relay or a network.
One test exists for an otherwise silent failure: a component receives the intersection of what it requests and what the coordinator grants, so a requirement in
miakapp.yamlthatstateAccessdoes not cover is not a publication error — the path simply never arrives.Also fixes
canonicalHttpsUrldemanded byte-for-byte URL round-tripping, andURLre-serializes a bare origin with a trailing slash. Every origin-only control plane was rejected, includinghttps://control.example.test, which is RFC 0004 §3's own example issuer.Verification
bun run check:packages— 31 CLI tests, 38 component tests, 8 template tests, plus the offline artifact check on a real bundle.bun testat the root: 218 passing.🤖 Generated with Claude Code