#210 feat: swamp extension verify — opaque extension-lifecycle verification primitive
Opened by stack72 · 5/1/2026
Problem
The swamp-extension-model and swamp-troubleshooting skills currently tell agents to invoke deno check, deno test, deno fmt, and deno lint directly when developing an extension. This works on a developer's box that already has a system Deno, but it has two underlying problems:
- Implementation lock-in. Inviting agents (and humans) to invoke Deno directly implicitly contracts that the runtime is Deno, that its CLI surface is part of swamp's public API, and that flag/output changes across Deno major versions are swamp's problem to absorb. Any future change to the bundled runtime — version bump with breaking CLI changes, or a swap to a different runtime entirely — becomes a user-visible break.
- Version skew on extension code. Extensions are bundled and executed by swamp using its bundled deno. Tests run on a developer's system Deno are tested against a different runtime than the one swamp will actually use in production. Most of the time the difference is invisible; sometimes it surfaces as
npm:resolution drift, JSR caching differences, or stdlib semantics mismatches.
This is the architectural follow-up to #195, which is being closed won't-fix. #195 proposed exposing ~/.swamp/deno/deno (or a swamp deno test passthrough) so agents could find swamp's bundled runtime when no system Deno was on PATH. That fix would have made both problems above worse, not better — it would have publicly committed swamp to "the bundled runtime is Deno, the binary lives at this path."
The right shape is a swamp-owned operation that runs the extension developer loop without exposing any Deno-shaped surface.
Proposed solution
Add swamp extension verify <manifest-or-extension-path> as an opaque extension-lifecycle verification primitive.
What it does (internally, today)
- Type check (
deno checkagainst the extension file using the bundled deno + swamp's import-map context) - Lint (
deno lint) - Format check (
deno fmt --check) - Run colocated unit tests (
*_test.tsnext to the extension file, using the bundled deno with the same permission policy swamp uses for execution) - Quality rubric scoring (the same checks
swamp extension push --dry-runalready runs)
All of these already exist somewhere in swamp's internals (src/libswamp/extensions/fmt.ts, quality.ts, push.ts, EmbeddedDenoRuntime). verify is composition over existing infra, not new infrastructure.
What it deliberately does NOT do
- Expose any Deno flags, paths, or runner semantics.
- Accept arbitrary test-runner options (
--filter,--reporter,--shuffle,--parallel,--no-check). The escape hatch for that is "bring your own Deno." - Become a generic Deno wrapper (
swamp deno <args>). - Run smoke tests against live APIs — that's the existing
swamp model method runsmoke-testing protocol, separate concern.
The principle: the primitive must be extension-shaped, not deno-shaped. If it ever drifts toward swamp extension test, swamp extension check, swamp extension lint, etc. — separate per-tool subcommands — we've just rebranded the Deno CLI under a swamp prefix and inherited every flag the original surface had. That's worse than direct exposure: same lock-in, more code to maintain.
CLI shape
swamp extension verify <manifest-or-extension-path>
--json # structured output
--skip-tests # quick lint-only checks during iteration
--skip-quality # skip rubric scoring during iterationDefault exit codes: 0 = all stages pass, non-zero = at least one stage failed. JSON mode returns a { stages: [{ name, passed, output }], passed: bool } envelope so CI and agents can pivot on individual failures.
Skill update (in scope for the same PR)
Once verify exists, every place in .claude/skills/swamp-extension-model/ and .claude/skills/swamp-troubleshooting/ that currently says deno check / deno test / deno fmt / deno lint against extension code flips to swamp extension verify. That's a sweep, not a one-line edit, and it's the actual user-visible value of the new primitive — without it, agents keep reaching for the raw Deno commands and the abstraction does nothing.
The bundled-deno path (~/.swamp/deno/deno) stays undocumented and unsupported as a public path.
Alternatives considered
- Expose
~/.swamp/deno/denodirectly in skills. The original ask in #195. Rejected: locks swamp into "the runtime is Deno, the binary is at this path" forever. swamp deno <args>passthrough command. Same lock-in as the path, just dressed up as a swamp subcommand. Every Deno flag becomes part of swamp's contract.- Per-tool subcommands (
swamp extension test,swamp extension check,swamp extension fmt,swamp extension lint). Rebrands the Deno CLI under a swamp prefix without removing the lock-in. - Tell agents to install a system Deno. The "do nothing" option for the bundled binary, but it leaves the version-skew problem for extension tests unsolved and adds friction for users who'd rather not.
- Fold test execution into
swamp extension push --dry-run. Tempting because--dry-runalready runs quality checks. Rejected becauseverifyshould be runnable mid-development, not only as a pre-publish step. (Worth checking during triage that--dry-runandverifyend up sharing implementation rather than duplicating it.)
Open questions to settle during triage
- Test discovery rule. Colocated
*_test.tsnext to the extension file is the obvious answer, but extensions sometimes have shared_lib/test fixtures. Need to define what counts as "the extension's tests." - Permissions. What
--allow-*set shouldverifypass to the bundled deno when running tests? Probably--allow-all(matching how swamp executes models), but worth being explicit so we don't surprise users whose tests touch the network or filesystem. - Bundle cache interaction. Should
verifyinvalidate the model's bundle cache, run tests against the cached bundle, or run against source? Source is the obvious answer for tests-of-the-source; bundle is what runs in production. Possibly both in different stages. - Relationship to
swamp extension push --dry-run.--dry-runalready runs quality checks. Eitherverifyis--dry-runminus the upload, or--dry-runbecomesverify+ upload simulation. Worth deduping rather than ending up with two near-identical commands. - Skill rewrite scope. The sweep across
swamp-extension-modelandswamp-troubleshootingshould land in the same PR as the new primitive. The exact list of files touched should fall out during planning.
Impact
Closes the architectural gap that #195 surfaced. After this lands:
- The original incident from #195 (an agent on a no-system-deno machine couldn't run
deno test) becomes a non-issue: the agent runsswamp extension verify, swamp owns the runtime invocation, no Deno path lookup needed. - swamp keeps the freedom to upgrade the bundled Deno (or replace it) without coordinating with users.
- Extension tests run on the runtime they'll actually execute on in production, eliminating the version-skew failure mode.
Open
No activity in this phase yet.