Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a6d471b
feat: extend bridge core models
pedronauck Apr 15, 2026
1f3b432
feat: scope bridge runtime by provider
pedronauck Apr 15, 2026
ef3fd56
feat: expand bridge v1 event contracts
pedronauck Apr 15, 2026
815bcda
feat: add provider-scoped bridge host api
pedronauck Apr 15, 2026
39a4649
feat: add shared bridge sdk runtime core
pedronauck Apr 15, 2026
a8942fc
feat: expose bridge provider config in shared APIs
pedronauck Apr 15, 2026
9fc399c
feat: update bridge web management
pedronauck Apr 15, 2026
e647bff
feat: add provider-scoped bridge conformance harness
pedronauck Apr 15, 2026
958160f
feat: add telegram bridge provider
pedronauck Apr 15, 2026
a350cb7
feat: implement slack bridge provider
pedronauck Apr 15, 2026
e79507e
test: harden slack integration assertions
pedronauck Apr 15, 2026
86da29f
feat: implement discord bridge provider
pedronauck Apr 15, 2026
04e3439
feat: implement whatsapp bridge provider
pedronauck Apr 15, 2026
029e81f
feat: add teams bridge provider
pedronauck Apr 15, 2026
e1e1afa
feat: add google chat bridge provider
pedronauck Apr 15, 2026
2442cf9
feat: add github bridge provider
pedronauck Apr 15, 2026
10a938f
feat: implement linear bridge provider
pedronauck Apr 15, 2026
03bd557
test: add bridge provider conformance matrix
pedronauck Apr 15, 2026
8ffc494
docs: update prd
pedronauck Apr 15, 2026
43b8e84
fix: qa round
pedronauck Apr 15, 2026
d4635d6
fix: remediate bridge adapter review batch
pedronauck Apr 15, 2026
0f5a85e
docs: update
pedronauck Apr 15, 2026
6d739e4
fix: web gaps
pedronauck Apr 15, 2026
6061f47
fix: resolve bridge adapter review batch
pedronauck Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
39 changes: 39 additions & 0 deletions .codex/plans/2026-04-15-bridge-secret-resolution-env-refs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Stock Bridge Secret Resolution via Env Refs

## Summary

- Make persisted bridge secret bindings usable in the stock daemon by wiring a default env-backed `BridgeSecretResolver` into normal daemon construction.
- Keep bridge secret bindings reference-only. In the stock binary, `vault_ref` supports only `env:NAME`; this change does not add raw secret values to the bridge HTTP/UDS surface.
- Fail early on unsupported ref syntax when a binding is written, and fail clearly on missing env values during boot/restart with an actionable error instead of `daemon: bridge secret resolver is required`.

## Key Changes

- **Daemon composition**
- Add a built-in env-backed resolver in `internal/daemon` that uses `Daemon.getenv`.
- Install it by default in the stock path when `WithBridgeSecretResolver(...)` is not provided.
- Preserve `WithBridgeSecretResolver(...)` as the override for tests and future backends.

- **Binding validation and contract**
- Define the stock-daemon `vault_ref` format as `env:NAME`.
- Add stock-path validation so `PUT /bridges/:id/secret-bindings/:binding_name` rejects empty env names and unsupported schemes such as generic `vault://...`.
- Keep the existing `BridgeSecretResolver` interface stable; add an optional validator capability in the daemon path so the default resolver can validate refs before persistence without forcing an interface break for custom resolvers.
- Update contract comments/OpenAPI descriptions and examples to state that the stock daemon currently supports `env:` refs only.

- **Runtime behavior**
- Resolve `env:` bindings during bridge runtime launch and pass the resolved values into `InitializeBridgeBoundSecret` exactly as today.
- On missing or empty env vars, return a precise config/auth error that names the binding and env var; keep the current lifecycle rollback behavior unchanged for this fix.
- Do not change provider-scoped runtime handshake shape, extension ownership, or bridge lifecycle orchestration beyond replacing the missing-resolver failure with real stock resolution.

## Test Plan

- Unit: env ref parsing/validation accepts `env:TG_TOKEN` and rejects empty, malformed, or unsupported refs.
- Unit: stock daemon composition installs the default env resolver only when no custom resolver is injected.
- Unit: bridge secret binding writes reject unsupported refs in the stock path and still accept valid `env:` refs.
- Integration: persisted `env:` bindings resolve during boot/restart and appear in the provider initialize handshake bound-secret payload.
- Integration: missing env vars fail with the new actionable error message, not `bridge secret resolver is required`.

## Assumptions

- No new raw-secret write API or CLI is added in this fix.
- `vault_ref` remains the field name to avoid broader contract churn, even though stock support is limited to `env:` refs for now.
- A future AGH-managed secret store can be introduced later as another resolver backend and ref scheme without changing the bridge launch handshake again.
79 changes: 79 additions & 0 deletions .codex/plans/2026-04-15-bridge-web-e2e.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Bridge Web E2E Closure

## Summary

- Close `/bridges` as an operator-ready surface: edit existing bridges, bind secrets, control lifecycle, and reflect live bridge health/status.
- Keep the detail panel as the operational cockpit; do not add a separate settings page.
- Land missing shared-contract work first so the web can consume the feature through generated types instead of ad hoc request code.

## Key Changes

### Shared API / Backend

- Publish missing secret-binding endpoints in the shared OpenAPI spec and regenerate generated clients/types:
- `GET /api/bridges/{id}/secret-bindings`
- `PUT /api/bridges/{id}/secret-bindings/{binding_name}`
- `DELETE /api/bridges/{id}/secret-bindings/{binding_name}`
- Add bridge-health SSE endpoints in HTTP and UDS:
- `GET /api/bridges/health/stream`
- Implement `StreamBridgeHealth` in `internal/api/core`, reusing the existing SSE helper pattern and emitting:
- one initial `snapshot`
- subsequent `snapshot` events only when bridge health changes
- one `error` event before exit if polling fails
- Keep the SSE contract out of OpenAPI, matching the project's existing stream convention.

### Web Data Layer

- Extend bridge system types with update payloads, secret-binding payloads, and bridge health stream snapshot types.
- Add adapters for:
- `updateBridge`
- `listBridgeSecretBindings`
- `putBridgeSecretBinding`
- `deleteBridgeSecretBinding`
- `enableBridge`
- `disableBridge`
- `restartBridge`
- Add query keys/options/hooks for secret bindings and mutation hooks for update, lifecycle, and secret CRUD.
- Add a dedicated bridge health stream hook that consumes `/api/bridges/health/stream` and patches React Query cache snapshots without replacing the normal queries as the source of truth.

### Web UI

- Reuse the existing bridge form model so create and edit share the same mutable bridge fields.
- Add an edit dialog for mutable bridge fields:
- `display_name`
- `dm_policy`
- `routing_policy`
- `provider_config`
- `delivery_defaults`
- Upgrade the detail panel into the full operational surface with:
- `Edit`
- `Enable` / `Disable`
- `Restart`
- inline secret-slot binding rows
- Use an env-first secret UX for the stock daemon:
- operator types `AGH_BRIDGE_*`
- UI submits `vault_ref: env:NAME`
- UI fixes `kind` to `slot.name`
- Show effective live status using `health.status` when present, falling back to persisted `bridge.status`.
- After config or secret changes, mark the bridge as restart-required until a successful restart or enable clears the hint.

## Test Plan

- Add backend tests for the new spec entries and bridge health stream behavior.
- Add web adapter and hook tests for update, lifecycle, secret CRUD, and SSE-driven cache updates.
- Add component and route integration tests for:
- editing a bridge
- binding/removing secrets
- lifecycle controls
- health/status updates from the SSE stream
- Run:
- `make web-lint`
- `make web-typecheck`
- `make web-test`
- `make verify`

## Assumptions

- The stock daemon path is the target UX, so secrets are optimized around `env:NAME`.
- Realtime means live bridge health/status updates, not live route streaming.
- Secret-binding CRUD must be added to the shared spec/codegen because the endpoints exist in the server but are currently absent from generated web types.
9 changes: 9 additions & 0 deletions .compozy/tasks/bridge-adapters/_meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
created_at: 2026-04-15T03:44:21.867482Z
updated_at: 2026-04-15T13:53:33.130525Z
---

## Summary
- Total: 17
- Completed: 17
- Pending: 0
34 changes: 17 additions & 17 deletions .compozy/tasks/bridge-adapters/_tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@

| # | Title | Status | Complexity | Dependencies |
|---|-------|--------|------------|--------------|
| 01 | Extend bridge core models, persistence, and provider manifests | pending | critical | - |
| 02 | Redesign provider-scoped bridge runtime handshake and daemon lifecycle | pending | critical | task_01 |
| 03 | Expand bridge v1 event and delivery contracts | pending | high | task_01 |
| 04 | Implement provider-scoped Host API instance management and authorization | pending | critical | task_01, task_02, task_03 |
| 05 | Build shared internal/bridgesdk runtime core and ingress hardening | pending | critical | task_02, task_03, task_04 |
| 06 | Expose provider metadata and provider_config through shared bridge APIs and OpenAPI | pending | high | task_01 |
| 07 | Update web bridge management for provider config, secret slots, and DM policy | pending | high | task_06 |
| 08 | Replace the Telegram reference path with a provider-scoped conformance harness | pending | high | task_02, task_04, task_05 |
| 09 | Implement the Telegram provider extension | pending | high | task_05, task_08 |
| 10 | Implement the Slack provider extension | pending | high | task_05, task_08 |
| 11 | Implement the Discord provider extension | pending | high | task_05, task_08 |
| 12 | Implement the WhatsApp provider extension | pending | high | task_05, task_08 |
| 13 | Implement the Microsoft Teams provider extension | pending | high | task_05, task_08 |
| 14 | Implement the Google Chat provider extension | pending | high | task_05, task_08 |
| 15 | Implement the GitHub provider extension | pending | high | task_05, task_08 |
| 16 | Implement the Linear provider extension | pending | high | task_05, task_08 |
| 17 | Add cross-provider multi-instance recovery and conformance coverage | pending | critical | task_09, task_10, task_11, task_12, task_13, task_14, task_15, task_16 |
| 01 | Extend bridge core models, persistence, and provider manifests | completed | critical | - |
| 02 | Redesign provider-scoped bridge runtime handshake and daemon lifecycle | completed | critical | task_01 |
| 03 | Expand bridge v1 event and delivery contracts | completed | high | task_01 |
| 04 | Implement provider-scoped Host API instance management and authorization | completed | critical | task_01, task_02, task_03 |
| 05 | Build shared internal/bridgesdk runtime core and ingress hardening | completed | critical | task_02, task_03, task_04 |
| 06 | Expose provider metadata and provider_config through shared bridge APIs and OpenAPI | completed | high | task_01 |
| 07 | Update web bridge management for provider config, secret slots, and DM policy | completed | high | task_06 |
| 08 | Replace the Telegram reference path with a provider-scoped conformance harness | completed | high | task_02, task_04, task_05 |
| 09 | Implement the Telegram provider extension | completed | high | task_05, task_08 |
| 10 | Implement the Slack provider extension | completed | high | task_05, task_08 |
| 11 | Implement the Discord provider extension | completed | high | task_05, task_08 |
| 12 | Implement the WhatsApp provider extension | completed | high | task_05, task_08 |
| 13 | Implement the Microsoft Teams provider extension | completed | high | task_05, task_08 |
| 14 | Implement the Google Chat provider extension | completed | high | task_05, task_08 |
| 15 | Implement the GitHub provider extension | completed | high | task_05, task_08 |
| 16 | Implement the Linear provider extension | completed | high | task_05, task_08 |
| 17 | Add cross-provider multi-instance recovery and conformance coverage | completed | critical | task_09, task_10, task_11, task_12, task_13, task_14, task_15, task_16 |
37 changes: 37 additions & 0 deletions .compozy/tasks/bridge-adapters/memory/MEMORY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Workflow Memory

Keep only durable, cross-task context here. Do not duplicate facts that are obvious from the repository, PRD documents, or git history.

## Current State

## Shared Decisions
- Task 01 establishes the canonical daemon bridge instance shape with typed `dm_policy`, `provider_config`, and structured degradation metadata persisted in `bridge_instances`, while provider manifests now surface `secret_slots` plus optional `config_schema` hints.
- Task 02 makes the bridge initialize handshake provider-scoped: `runtime.bridge` now carries `runtime_version`, `provider`, `platform`, and `managed_instances[]`, with each managed instance snapshot owning its bound secrets.
- Daemon-owned bridge lifecycle and secret binding resolution remain authoritative; provider runtimes receive clone-safe launch snapshots rather than live mutable bridge state.
- Task 04 expands the bridge Host API to provider-scoped ownership: runtimes now use `bridges/instances/list`, while `bridges/instances/get` and `bridges/instances/report_state` require an explicit `bridge_instance_id` tied to the negotiated runtime ownership set.
- `bridges/instances/report_state` now updates structured degradation atomically with lifecycle state changes; non-degraded statuses automatically clear stored degradation when the contract no longer allows it.
- Task 05 establishes `internal/bridgesdk` as the shared bridge provider substrate; future bridge runtimes should compose its runtime, Host API client, ingress guards, dedup, batching, and classified retry helpers rather than copying `telegram-reference` boot logic.
- Task 08 replaces the old single-instance `telegram-reference` reference path with a provider-scoped conformance runtime built on `internal/bridgesdk`; the reusable harness contract now requires ownership evidence from `bridges/instances/list` plus explicit `bridges/instances/get`, per-instance state markers, and provider-scoped delivery/ingress validation across multiple managed instances.
- Task 09 lands the first real production provider under `extensions/bridges/telegram`; later providers should mirror the shared `internal/bridgesdk` runtime pattern rather than promoting example adapters into production paths.
- Task 10 lands the first interaction-heavy production provider under `extensions/bridges/slack`; later providers can reuse the same `internal/bridgesdk` runtime shape for signed webhook ingress, typed `command`/`action`/`reaction` mapping, and provider-scoped conformance validation.
- Task 11 confirms that a second interaction-heavy provider can stay inside bridge v1 by returning Discord’s required inline interaction ACKs immediately and deferring Host API ingestion asynchronously behind the shared `internal/bridgesdk` ingress guards.
- Task 14 confirms the shared provider runtime can absorb Google Chat’s two ingress families (direct webhook + Pub/Sub push) inside bridge v1 without any daemon protocol fork.
- Task 15 confirms the shared provider runtime can route multiple GitHub bridge instances through one provider-scoped `/github` webhook endpoint while separating repository-scoped ownership and PAT vs App delivery behavior, including App installation-aware delivery using fixed or cached installation IDs.
- Task 16 confirms the shared provider runtime can keep provider-owned mode/auth branching entirely inside `provider_config`; Linear routes one shared webhook endpoint by `(organization_id, mode)` and uses provider-local `auth_mode` (`api_key` vs OAuth client-credentials) without changing daemon-global bridge semantics.
- Task 07 keeps web bridge management progressive: `provider_config` is operator-edited as a validated JSON object because provider manifests currently expose only `config_schema`/`version` hints and `secret_slots`, not field-level form definitions.
- `internal/bridgesdk` instance-cache synchronization must preserve launch-time bound secrets from the initialize handshake because the provider-scoped Host API refreshes bridge instance state only and does not resend secret material.
- Webhook-based provider integration tests can use the shared extension harness by supplying fixed listen/API-base env overrides and driving real HTTP requests into the spawned subprocess instead of relying on the reference adapter’s file-based update stream.
- Task 17 makes the harness record `bridges/instances/report_state` directly at the Host API boundary so classified recovery transitions emitted via `internal/bridgesdk.Session.ReportClassifiedError` are visible to shared conformance tests even when providers do not write explicit state-marker side effects.
- Task 17 defines the reusable conformance matrix as one aggregated row per provider/platform; multiple scenario summaries for the same provider should merge targets and managed-instance outcomes instead of being treated as duplicates.

## Shared Learnings
- Shared bridge contract changes flow through generated artifacts; after editing exported bridge structs, rerun the repo codegen path so `openapi/agh.json` and `sdk/typescript/src/generated/contracts.ts` stay aligned with the Go source.
- Platform routing policies must match the provider’s actual routing dimensions; Telegram forum/group traffic uses `group_id + thread_id`, so conformance fixtures must not require `peer_id` for those routes.
- Mixed Slack interaction fixtures should also align routing policy with the emitted payload dimensions; slash commands do not include thread identity, so multi-family Slack conformance runs should not force thread-based routing.
- Provider tests that boot a subprocess-backed bridge runtime and then swap runtime collaborators after `initialize` must wait until the async `afterInitialize` path has finished publishing instance state first; otherwise `go test -race` can hit factory/teardown races and hang on provider waitgroups.
- Teams confirmed the same routing rule under Bot Framework: keep channel-scoped ingress fixtures on `group_id + thread_id`, and treat proactive DM delivery as a separate path that relies on cached or configured `tenant_id` plus `service_url`.

## Open Risks
- `go test -tags integration ./internal/extension` currently fails in the unrelated `TestReferenceExtensionsEndToEnd` path because `sdk/examples/prompt-enhancer/node_modules/.bin/tsc` resolves outside the example root and trips the extension install symlink guard. Task-specific bridge manifest integration coverage still passes.

## Handoffs
39 changes: 39 additions & 0 deletions .compozy/tasks/bridge-adapters/memory/task_01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Task Memory: task_01.md

Keep only task-local execution context here. Do not duplicate facts that are obvious from the repository, task file, PRD documents, or git history.

## Objective Snapshot
- Extend the bridge core model, global DB persistence, and provider manifest metadata with `provider_config`, typed DM policy, structured degradation data, required secret slots, and optional config schema/version hints.
- Keep scope limited to model/storage/manifest concerns; runtime handshake and Host API redesign stay out of scope for this task.

## Important Decisions
- Use the TechSpec/ADR-approved design directly; no separate design phase for this run.
- Keep `provider_config` distinct from `delivery_defaults` all the way through validation and persistence.
- Treat broad package coverage in `internal/extension` and `internal/store/globaldb` as pre-existing package debt; verify the task against the bridge/manifest/globaldb task surfaces plus the repository `make verify` gate.

## Learnings
- Current `BridgeConfig` only carries `platform` and `display_name`.
- Current `BridgeInstance` and `bridge_instances` storage only carry `routing_policy` and `delivery_defaults`; the new provider-scoped fields do not exist yet.
- Task-specific verification now passes: `make verify`, `go test ./internal/daemon -run TestBridgeRuntimeListProviders`, `go test -tags integration ./internal/extension -run TestLoadManifestBridgeMetadataRoundTrip`, and `go test -tags integration ./internal/store/globaldb -run 'TestGlobalDBBridgeInstanceRoundTripAcrossReopen|TestOpenGlobalDBMigratesLegacyBridgeInstancesWithoutProviderConfig'`.
- Package-wide `go test -cover ./internal/extension ./internal/store/globaldb` still reports `78.2%` and `78.5%` because those packages contain older unrelated surfaces outside this task’s bridge/manifest changes.

## Files / Surfaces
- `internal/bridges/types.go`
- `internal/bridges/registry.go`
- `internal/store/globaldb/global_db.go`
- `internal/store/globaldb/global_db_bridge.go`
- `internal/extension/manifest.go`
- `internal/daemon/bridges.go`
- `internal/bridges/types_test.go`
- `internal/store/globaldb/global_db_bridges_test.go`
- `internal/store/globaldb/global_db_bridges_integration_test.go`
- `internal/extension/manifest_test.go`
- `internal/extension/manifest_integration_test.go`
- `internal/extension/registry_test.go`
- `internal/store/globaldb/global_db_extra_test.go`

## Errors / Corrections
- `go test -tags integration ./internal/extension ./internal/store/globaldb` fails outside task scope because `reference_integration_test.go` installs `sdk/examples/prompt-enhancer`, whose `node_modules/.bin/tsc` symlink escapes the example root and is rejected by the extension source guard.

## Ready for Next Run
- Task 01 implementation and verification are complete; leave tracking-only artifacts out of the auto-commit unless the workflow explicitly requires them.
Loading