Skip to content

Release: merge dev to main#1061

Merged
zbigniewsobiecki merged 20 commits intomainfrom
dev
Mar 26, 2026
Merged

Release: merge dev to main#1061
zbigniewsobiecki merged 20 commits intomainfrom
dev

Conversation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

Summary

Enables cascade projects integration-set --category alerting and adds migration 0047 for the DB CHECK constraint.

Test plan

  • CI passed on feature PR
  • CI passes on this merge PR
  • Post-deploy: run Sentry integration setup via CLI

🤖 Generated with Claude Code

aaight and others added 20 commits March 24, 2026 18:29
…ingIntegration class (#1040)

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Cascade Bot <bot@cascade.dev>
…n categories (#1043)

* refactor(triggers): use registry-driven validation for all integration categories

* fix(test): bootstrap integration registry in integration-validation test

The new registry-driven validateIntegrations() calls
integrationRegistry.getByCategory() which requires integrations to be
registered first. Without importing the bootstrap module, the registry
is empty and all validations report "none is registered" instead of
actually checking project credentials against the database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…section (#1044)

* docs(integrations): add integration architecture guide and CLAUDE.md section

* docs(integrations): fix Step 4 webhook route example and stale trigger registration reference

- Correct webhook route pattern in README Step 4: URL is /<provider>/webhook
  (not /webhook/<provider>), handler uses createWebhookHandler() config object,
  and processRouterWebhook() signature is (adapter, payload, triggerRegistry) with
  no Hono context or type string parameter
- Update CLAUDE.md "Adding New Triggers" to reference src/triggers/builtins.ts
  (via registerBuiltInTriggers()) instead of the stale src/triggers/index.ts,
  and add a cross-reference to the new README guide

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…#1045)

* feat(projects): expose snapshot config across API, CLI, and dashboard

* fix(dashboard): send boolean false when snapshot checkbox is unchecked

`snapshotEnabled || null` coerced `false` to `null`, meaning unchecking
the checkbox sent "clear override / use default" instead of "explicitly
disable". Pass the bare boolean, matching the pattern used by
`runLinksEnabled` on the line above.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
… snapshots (#1046)

* feat(router): add snapshot manager and capture/reuse worker container snapshots

* fix(router): prevent container orphan when snapshotEnabled=true but workItemId is undefined

- Fix container leak: broaden success-path guard from
  `snapshotEnabled && projectId && workItemId` to just `snapshotEnabled`,
  so removeContainer() is always reached for snapshot-enabled runs regardless
  of whether workItemId is present — consistent with the .catch() error path.
  Commit is still gated on `projectId && workItemId` inside the block.
- Add TTL eviction to getSnapshot(): expired entries (age > snapshotDefaultTtlMs)
  are eagerly deleted and undefined is returned, preventing unbounded Map growth.
- Mock routerConfig in snapshot-manager unit tests and add TTL eviction tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(router): honour per-project snapshotTtlMs and remove orphaned snapshot containers

- Add `ttlMs` parameter to `getSnapshot()` so callers can pass a
  per-project TTL override instead of always using the global default.
  `resolveSpawnSettings()` now reads `projectCfg?.snapshotTtlMs` and
  forwards it to `getSnapshot()`, so per-project TTL configuration
  via the DB/dashboard takes effect.

- Call `container.remove()` after `container.stop()` in orphan cleanup
  so stopped snapshot containers (AutoRemove=false) are not left on disk.
  `remove()` failure is swallowed; on non-snapshot containers where
  AutoRemove=true Docker may have already removed them, making the
  no-op safe.

- Update tests: existing orphan-cleanup mocks gain a `remove` mock;
  new tests verify remove behaviour and that per-project TTL is passed
  through the call chain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
#1047)

* feat(worker): refresh snapshot workspace instead of recloning on reuse

* fix(repository): tighten findSnapshotWorkspaceDir to require numeric timestamp suffix

Prevents false-positive matches when one project ID is a prefix of another
(e.g. project "foo" matching directory "cascade-foo-bar-<timestamp>").
Now verifies the suffix after the project-ID prefix is all digits, matching
only directories created by createTempDir (cascade-<projectId>-<Date.now()>).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
… invalidation (#1048)

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Cascade Bot <bot@cascade.dev>
)

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Cascade Bot <bot@cascade.dev>
…ook-execution (#1053)

Co-authored-by: Cascade Bot <bot@cascade.dev>
)

When a snapshot Docker image is deleted externally while the in-memory
SnapshotManager still holds a reference to it, the next job dispatch
crashed with a Docker 404 and left the card in "failed" state. Every
subsequent trigger attempt hit the same 404 until the router was restarted.

Fix: detect the missing-image error in spawnWorker, invalidate the stale
registry entry, and transparently retry with the base worker image so the
run proceeds without any user intervention.

Changes:
- isImageNotFoundError uses Docker's typed statusCode property (statusCode
  === 404) as the primary check rather than fragile substring matching
- Extract docker create/start/monitor logic into createAndMonitorContainer
  so the fallback can retry without duplicating the spawn setup
- Introduce ContainerLaunchConfig interface to reduce the helper from 11
  positional params to 7, making the two call sites clearly show what
  differs between primary and fallback (only workerImage and workerEnv)
- Remove dead snapshotReuse param from createAndMonitorContainer (it was
  never referenced inside the function; the env already has
  CASCADE_SNAPSHOT_REUSE baked in before the call)
- Include staleImage in captureException extra context on fallback failure
  so both errors can be correlated in Sentry

Tests:
- Hoist mockInvalidateSnapshot so it is properly reset between tests
- Verify invalidateSnapshot is called in both the success and failure paths
- Add test: fallback container preserves snapshotEnabled (AutoRemove=false)
- Add test: 404 on base image (snapshotReuse=false) propagates without
  retry and without invalidating any snapshot

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The snapshot workspace was being deleted by the worker's cleanup step
before the process exited, so every `docker commit` captured an empty
/workspace. On the next trigger the router logged a snapshot hit but
`findSnapshotWorkspaceDir` returned null and fell back to a full clone +
setup, defeating the purpose of snapshots entirely.

Root cause: `cleanupAgentResources` calls `cleanupTempDir(repoDir)` in a
`finally` block; the router calls `docker commit` only after
`container.wait()` resolves — after the workspace is already gone.

Fix: introduce a `CASCADE_SNAPSHOT_ENABLED=true` worker env var (set by
the router whenever `snapshotEnabled=true`). When the worker sees this
flag it skips workspace deletion so the directory is present in the image
when the router commits the container.

- `worker-env.ts`: add `snapshotEnabled` param → push env var
- `container-manager.ts`: pass `snapshotEnabled` to both the initial and
  404-fallback `buildWorkerEnvWithProjectId` calls
- `cleanup.ts`: skip `cleanupTempDir` when `CASCADE_SNAPSHOT_ENABLED=true`
- tests: new `cleanup.test.ts` + expand `worker-env.test.ts`

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…onfigs (#1056)

* feat(harness): show credentials for all in-use engines across agent configs

* refactor(harness): extract ENGINE_SECRETS to shared module to eliminate duplicate credential mapping

Moves the engine-to-credential mapping out of project-harness-form.tsx and
into a new engine-secrets.ts shared module. project-agent-configs.tsx now
derives its engineCredentialKeys map directly from ENGINE_SECRETS at module
load time, so both files always agree on which credentials belong to which
engine.

Also adds llmist to the OPENROUTER_API_KEY engines list so the Harness tab
credential field appears for llmist engine overrides — fixing the concrete
UX inconsistency where agent rows showed "Missing credentials" for llmist
but the Harness tab never showed an OPENROUTER_API_KEY field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ult indicator (#1057)

* feat(dashboard): per-engine tabs with credentials, settings, and default indicator

* fix(dashboard): address review feedback on engine credential descriptions

- Remove dead code: `defaultEngineLabel` variable left over after Select dropdown was removed
- Combine original secret description with sharing note instead of replacing it
- Map shared engine IDs to human-readable labels (engine.label) in "Also used by" note

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(deps): upgrade picomatch to 4.0.4 to resolve high-severity audit vulnerabilities

Fixes two high-severity CVEs in picomatch 4.0.3:
- GHSA-c2c7-rcm5-vvqj: ReDoS vulnerability via extglob quantifiers
- GHSA-3v7f-55p6-f55p: Method Injection in POSIX Character Classes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(dashboard): add reset-to-default affordance and remove duplicate save button

- Add "Reset to system default" link on the default engine tab when an engine
  is explicitly set, calling setAgentEngine('') to restore inherit behaviour
- Remove the duplicate Save Changes footer from the Model & Runtime card;
  a single save button in the Engine Settings & Credentials card footer
  now covers both cards via form="engine-runtime-form"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(work): add inline run duration bars to Work table rows

* fix(work): remove conflicting `flex` class from legend container

Remove unprefixed `flex` that conflicted with `hidden` at the base
breakpoint. Now `hidden` applies at mobile and `sm:flex` overrides at
the sm breakpoint, preventing non-deterministic display behaviour.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…int (#1060)

The Sentry alerting integration was fully implemented (module, triggers,
tRPC, web UI) but the CLI `integration-set` command rejected `--category
alerting` and the DB CHECK constraint didn't include the alerting/sentry
pair. This adds both so Sentry can be configured via the CLI.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@zbigniewsobiecki zbigniewsobiecki merged commit 0b31900 into main Mar 26, 2026
14 checks passed
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.

2 participants