Tags: swamp-club/swamp
Tags
fix(vault): write secret value to stdout without log formatting (#646) ( #1576) ## Summary - `vault read-secret` used `logger.info` to output the secret value, which prepended timestamp, level, and category formatting (e.g. `10:00:15.092 INF vault·read-secret "value"`) to stdout — breaking `$(swamp vault read-secret ...)` shell substitution - Switched to `writeOutput()` for undecorated stdout, matching the established pattern used by 15+ other renderers - Added unit tests for both log and JSON output modes ## Test Plan - [x] Reproduced the bug in a scratch repo: `swamp vault read-secret ... --force 2>/dev/null | od -c` showed log prefix in stdout - [x] Verified the fix: same command now shows only the raw secret value - [x] New unit tests pass: `deno run test src/presentation/renderers/vault_read_secret_test.ts` - [x] Full test suite passes (7032 passed, 1 pre-existing unrelated failure in `http_update_checker_test.ts`) - [x] `deno check`, `deno lint`, `deno fmt` all pass Closes #646 Co-authored-by: Blake Irvin <blakeirvin@me.com>
feat(cli): deduplicate model type describe output and add --compact m… …ode (#1575) ## Summary Fixes #615 — `model type describe --json` was duplicating all output specs into every method (40% bloat, 220KB for a 16-method type) and carrying a redundant `inputs` field identical to `arguments`. - **Hoist `dataOutputSpecs` to type level** in `TypeDescribeData`, removing per-method duplication. Matches the domain model where specs are defined on the type, not per-method. - **Add `--compact` flag** producing a minimal digest (~3-5KB): method names, descriptions, argument property names/types/required, output spec names only. Designed for the agent discovery hot path. - **Drop `inputs` field** from `MethodDescribeData` (was a byte-for-byte duplicate of `arguments`). - Update all renderers (`type_describe`, `model_get`, `model_method_describe`, `model_search`) and generators (`describe`, `get`, `create`, `method_describe`). - Update swamp, swamp-getting-started, and terminal-output skills to recommend `--compact --json` for discovery. ## Test Plan - [x] New unit tests for `toMethodDescribeData` (no specs/inputs), `buildDataOutputSpecs`, type-level specs in describe output, compact vs full JSON rendering - [x] All existing tests updated for new output shape - [x] `deno check` / `deno lint` / `deno fmt` / `deno run test` — 7037 tests pass, 0 failures - [x] `deno run compile` succeeds - [x] Filed UAT issue: swamp-club/swamp-uat#266 - [x] Filed docs issue: #651 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Magistr <umag@users.noreply.github.com> Co-authored-by: Magistr <umag@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix(reports): make reports.require failures loud (#640) (#1574) ## Summary Fixes #640, where a pulled extension report named in a workflow's `reports.require` was silently skipped — no error, no warning, no artifact — while `--report <name>` and local extension reports worked. The root symptom is that an unresolvable required report was dropped from the candidate set with zero diagnostics. This PR makes `reports.require` a real contract and closes two adjacent silent-skip paths found during investigation. ### Changes 1. **Loud unresolvable requires** (`report_execution_service.ts`): after the lazy-promotion pass, any `reports.require` name still absent from the registry now logs a warning naming the report and the remedy, emits a `report_failed` event, persists a searchable error artifact (so `swamp report search` surfaces it), and counts as a failure — which fails a `swamp model method run`, matching how a required report that loads then throws already behaves. Names also in `reports.skip` are exempt. A new `emitUnresolvableRequireFailures` parameter lets the method+model scope callers dedup the warning. 2. **Surfaced swallowed errors**: report-runner catch blocks in `workflow_report_runner.ts` and `method_report_runner.ts` now log at `warn` instead of `debug`, so a broken required bundle is visible at the default log level (errors are still swallowed so a broken report can't mask the run result). 3. **`reportFilterOptions` defaults to `{}`** in `WorkflowExecutionService.run`/`resume`. `swamp workflow resume` never threaded the CLI report flags, so resumed runs silently skipped **all** workflow- and step-scope reports, required ones included. An absent filter now means "no filtering", not "no reports". ### Behavior change (intentional) A model definition or workflow with a missing/typo'd required report changes from **silent success** to a **failed run**. This is the point of the fix — a required report that never runs should not pass silently. ### Note on the original report We could not reproduce the silent skip on Linux, at the reporter's exact build (873563f) or at HEAD — the pulled `@webframp/terraform-drift-report` auto-executes via `require` on cold and warm catalog runs. `--report` is a pure narrowing filter over the same candidate set, so the reported symptom pair implies the repo state changed between runs (likely an `extension pull` repairing a broken catalog entry). A diagnostics request is posted on the issue; regardless of the original trigger, every path here is now loud. ## Test Plan - New unit tests in `report_execution_service_test.ts`: unresolvable require emits `report_failed` + counts a failure + persists an error artifact + does not throw + sibling reports still run; `emitUnresolvableRequireFailures=false` suppresses the duplicate; skip exempts the name; persistence failure still reports the failure. - New regression tests in `execution_service_test.ts`: `run()` executes workflow-scope required reports with no `reportFilterOptions`; step context defaults the options to `{}`. Two CONTRACT tests updated to reflect that the builtin workflow summary now emits on a bare `service.run()`. - Manual verification against a real pulled `@webframp/aws/terraform-drift` repo with the compiled binary: drift report still runs exactly once per run; a workflow/model requiring a nonexistent report now warns, emits `report_failed`, writes a `report search`-visible artifact, and exits non-zero. - `deno check`, `deno lint`, `deno fmt`, `deno run compile` clean. Full suite green except pre-existing unrelated failures on `main` (filed as #645). Docs: `design/reports.md` updated; manual-docs follow-up filed as swamp-club Lab #648. UAT coverage gap filed as swamp-club/swamp-uat#264. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: MELLENS <mellens@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
chore: streamline CLAUDE.md (#1569) ## Summary Deduplication and accuracy pass over CLAUDE.md (169 → 155 lines, single-file diff): - **Removed the skill-creator duplication.** The Skills section mandated loading `skill-creator` as a hard prerequisite, then inlined ~22 lines of content that skill already owns (structure diagram, frontmatter rules, 500-line limit, no-extraneous-files). That paid the context cost twice per session and created a drift hazard. The mandate stays, along with the genuinely repo-specific rules: uppercase `SKILL.md` and `deno fmt` after skill markdown edits. - **Verification commands stated once.** The check/lint/fmt commands appeared in Code Style, Commands, and Verification; `deno run compile` appeared in both Source Control and Verification. The Verification section is now the single canonical checklist. - **Added the skill testing workflow.** New skills-section note covering `npx tessl skill review` (≥ 90% average) and `deno run eval-skill-triggers` (promptfoo), accurately scoped: CI gates the bundled skills (`swamp`, `swamp-getting-started`) on these; for other skills they're hygiene, not a gate. Pointer to `design/skills.md` for the full pipeline. - **Reordered sections** so Commands flows into Verification and Architecture sits next to Testing. Earlier revisions of this branch also removed the `terminal-output` skill and updated the vendored `skill-creator` to latest upstream; both were reverted after review. terminal-output's SKILL.md is the only home of the output design-system conventions (not covered by `design/rendering.md`), and the new upstream skill-creator prescribes an eval workflow that competes with this repo's tessl/promptfoo pipeline — each needs its own follow-up before landing. ## Test Plan - `deno fmt --check` — clean - `deno lint` — clean - `deno run test` — 6898 passed, 0 failed - Verified the removed CLAUDE.md rules are stated in the vendored skill-creator (frontmatter, 500-line limit, no extraneous files) - Verified CI scoping claims against `scripts/review_skills.ts` (BUNDLED_SKILLS) and `evals/promptfoo/generate_config.ts` (hardcoded skill list) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
feat(extensions): allow extensions to declare resource specs (#1573) ## Summary Closes #619. - Adds an optional `resources` field to `UserExtensionSchema`, allowing `export const extension` to declare new resource specs on the target model type - Extends `modelRegistry.extend()` with a `resources?` parameter that merges with conflict detection (same pattern as methods, checks, and reports) - Updates `processSecondaryExport()` to pass extension-declared resources through to `extend()` Previously, extensions could add methods and checks but not resource specs — if an extension method needed a different output shape than the target model's existing resources, the only option was to build a standalone model. Now extensions can declare their own resources and write to them. ## Test Plan - Added 4 unit tests for `ModelRegistry.extend()` with resources: merge, conflict detection, preservation when absent, and adding to a model with no existing resources - Added 2 integration tests in `user_model_loader_test.ts`: extension resource merge via loader, and conflict detection via loader - Verified end-to-end in a scratch repo (`/tmp/swamp-repro-issue-619`): - **Before**: `swamp model method run my-widget audit` fails with `Undeclared resource spec 'audit' in model 'repro/widget'. Declared resource specs: state` - **After**: succeeds, writes audit data to the extension-declared `audit` resource with correct schema, garbage collection, and tags - `deno check`, `deno lint`, `deno fmt`, `deno run test`, `deno run compile` all pass Co-authored-by: Mark Jentz <jentz@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat(extensions): allow extensions to declare resource specs (#1573) ## Summary Closes #619. - Adds an optional `resources` field to `UserExtensionSchema`, allowing `export const extension` to declare new resource specs on the target model type - Extends `modelRegistry.extend()` with a `resources?` parameter that merges with conflict detection (same pattern as methods, checks, and reports) - Updates `processSecondaryExport()` to pass extension-declared resources through to `extend()` Previously, extensions could add methods and checks but not resource specs — if an extension method needed a different output shape than the target model's existing resources, the only option was to build a standalone model. Now extensions can declare their own resources and write to them. ## Test Plan - Added 4 unit tests for `ModelRegistry.extend()` with resources: merge, conflict detection, preservation when absent, and adding to a model with no existing resources - Added 2 integration tests in `user_model_loader_test.ts`: extension resource merge via loader, and conflict detection via loader - Verified end-to-end in a scratch repo (`/tmp/swamp-repro-issue-619`): - **Before**: `swamp model method run my-widget audit` fails with `Undeclared resource spec 'audit' in model 'repro/widget'. Declared resource specs: state` - **After**: succeeds, writes audit data to the extension-declared `audit` resource with correct schema, garbage collection, and tags - `deno check`, `deno lint`, `deno fmt`, `deno run test`, `deno run compile` all pass Co-authored-by: Mark Jentz <jentz@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: remote execution — orchestrator/worker fan-out, replacing execu… …tion drivers (#535) (#1547) ## Summary Implements remote execution per `design/remote-execution.md` (swamp issue #535): a single orchestrator fans workflow steps out across disposable **workers** — swamp binaries that dial home with a token, enroll, and execute dispatched method bodies with every durable capability proxied back to the orchestrator. Execution drivers (`raw`/`docker`, `driver:`/`driverConfig:`) are removed; isolation is now a worker deployment property selected with step placement. ### Control plane - Symmetric RPC protocol (`rpc.*` frames) shared by orchestrator and worker over the existing serve WebSocket; the legacy client protocol on the same listener is unchanged. - Worker gateway: enrollment (`<name>.<secret>` tokens, constant-time compare, enroll-once with reconnect-for-lifetime), per-worker reconnection grace windows, sliding data-plane session credentials, pool snapshots for scheduling. - Built-in models persisted as ordinary swamp data: `swamp/worker`, `swamp/enrollment-token` (vault-stored plaintext, `unused → enrolled → expired` + revocable), `swamp/step-lease` — all with bounded retention; transitions serialized in the orchestrator process. ### Data plane - Bearer-authenticated HTTP routes on the serve listener (h2 via ALPN under TLS): immutable artifact reads with strong ETags, lease-scoped writes through the existing data writers (declared-spec enforcement for free), per-request-durable line appends, writer sessions, bundle + co-located-asset fetch by content fingerprint. ### Worker runtime - `swamp worker connect <url> --token …` — repo-less dial-home with backoff reconnect. - Remote `MethodContext` proxy adapters covering the full capability inventory: reads/queries/vault/definitions/outputs over the control socket, bytes over the data plane, spool-on-finalize `getFilePath`, prefetched extension assets for synchronous `extensionFile()`, loud `UnsupportedOnRemoteWorkerError` for sync members. Method author APIs unchanged. - Per-dispatch environment snapshot (identity denylist: HOME/PATH/USER/…) applied and restored on every exit path; dispatches execute serially per worker. ### Scheduling & workflow integration - Step YAML gains `target:` / `labels:` / `platform:`; placed steps dispatch at the execution seam while checks, reports, output records, and follow-up actions stay at the orchestrator. `forEach` fans instances out across matching workers. - Direct target → labels → platform matching, deterministic tiebreak, queue-when-busy, fail-fast when unschedulable; no-write drops re-dispatch, write-then-drop fails the run. ### CLI - `swamp worker token create|list|revoke`, `swamp worker list`, `swamp worker connect` — log + json modes. ### Removal - `ExecutionDriver`, the registry/resolution chain, docker driver + runner, the driver extension kind, `--driver` flags, and `driver`/`driverConfig` schema fields are gone; old YAML fails with an actionable error. Bundling/fingerprinting machinery is retained (remote dispatch ships it). ### CLI run-through-server - `swamp workflow run`/`swamp model … method run` gain `--server <url>`: repo-less dispatch through the serve WebSocket, streaming events through the same renderers as a local run (lossless wire codec via `deserializeEvent`), Ctrl-C cancels server-side, exit codes match local. Additive terminal `done` protocol frame; direct-type-execution steps now work over serve. ## Test plan - [x] ~200 new unit tests across protocol, gateway, capability service, data plane, dispatch service, scheduler, worker runtime, built-in models, CLI - [x] End-to-end integration test: real WebSocket enrollment through the built-in token model against a real datastore + vault; dispatched method exercising writes/appends/reads/vault round-trip/environment shipping; lease lifecycle as queryable data; scheduling matrix; cancel propagation; data-plane auth; bad-token rejection - [x] `deno check`, `deno lint`, `deno fmt`, full `deno run test`, `deno run compile` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
fix(doctor): fix orphan cross-attribution for nested scoped extensions ( #628) (#1570) ## Summary - Fix `extractTopLevelRoot()` to handle nested scoped extension names with 3+ segments (e.g. `@swamp/aws/iam`, `@swamp/aws/s3`) - The function hardcoded a 2-segment assumption for scoped names, causing sibling nested extensions to extract to the same root directory and cross-attribute each other's files as false orphans - Add `extensionName` parameter so the function uses the actual segment count to determine the correct per-extension root depth Closes #628 ## Test Plan - [x] Unit tests for `extractTopLevelRoot` with 3-segment scoped names - [x] Integration test verifying sibling nested extensions don't cross-attribute orphans - [x] All existing tests updated and passing - [x] Reproduced the bug in a scratch repo (`/tmp/swamp-repro-issue-628`) and verified the compiled binary reports 0 false orphans after the fix - [x] `deno check`, `deno lint`, `deno fmt`, `deno run test` all pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: crudec <crudec@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix(doctor): fix orphan cross-attribution for nested scoped extensions ( #628) (#1570) ## Summary - Fix `extractTopLevelRoot()` to handle nested scoped extension names with 3+ segments (e.g. `@swamp/aws/iam`, `@swamp/aws/s3`) - The function hardcoded a 2-segment assumption for scoped names, causing sibling nested extensions to extract to the same root directory and cross-attribute each other's files as false orphans - Add `extensionName` parameter so the function uses the actual segment count to determine the correct per-extension root depth Closes #628 ## Test Plan - [x] Unit tests for `extractTopLevelRoot` with 3-segment scoped names - [x] Integration test verifying sibling nested extensions don't cross-attribute orphans - [x] All existing tests updated and passing - [x] Reproduced the bug in a scratch repo (`/tmp/swamp-repro-issue-628`) and verified the compiled binary reports 0 false orphans after the fix - [x] `deno check`, `deno lint`, `deno fmt`, `deno run test` all pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: crudec <crudec@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat(extensions): add --channel flag to yank and unyank commands (#1567) ## Summary Adds `--channel <stable|beta|rc>` to `swamp extension yank` and `swamp extension unyank` so maintainers can scope yank/unyank to a specific release channel instead of affecting all channels at once. - Without `--channel`: preserves today's all-channel behavior (backward compatible) - With `--channel stable`: yanks/unyanks only versions on the stable channel, leaving beta/rc discoverable - Channel validated using existing `ReleaseChannel.isValid()` - Confirmation prompts and log/JSON output reflect the channel scope Requires backend support from swamp-club/swamp-club#668. Closes #626 ## Test Plan - [x] Updated unit tests for `extensionYank` and `extensionUnyank` service functions - [x] Added channel-scoped test cases verifying channel is threaded to deps - [x] Updated `ExtensionApiClient` tests — channel included in POST body when provided, omitted when null - [x] All 6906 tests pass (`deno run test`), type check, lint, format clean - [x] Binary compiles (`deno run compile`) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Magistr <umag@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PreviousNext