Skip to content

feat: Add notification infrastructure with bell UI#1316

Merged
bokelley merged 9 commits into
mainfrom
bokelley/notifications
Mar 4, 2026
Merged

feat: Add notification infrastructure with bell UI#1316
bokelley merged 9 commits into
mainfrom
bokelley/notifications

Conversation

@bokelley

@bokelley bokelley commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add in-app notification system with polymorphic reference_id/reference_type pattern
  • Add bell icon with dropdown in nav that polls unread count every 30s
  • Add full notifications page at /community/notifications
  • Add notification triggers for connection requests, connection acceptances, and badge awards
  • Consolidate Slack DM delivery into notifyUser() service (removes duplicate DM functions)
  • Link working group members to their profile pages
  • Add rate limiting (120 req/min) on notification endpoints
  • Use design system CSS variables throughout

Context

Extracted from the community board exploration branch. The board concept needs more product thinking (how groups relate to Slack channels, whether it's a curated library or parallel forum). This PR ships the generic notification infrastructure that's valuable regardless of how the board evolves.

Test plan

  • Migration 259_notifications.sql runs clean on deploy
  • Bell icon appears in nav for logged-in users, hidden for visitors
  • Clicking bell opens dropdown with unread notifications
  • "View all" link navigates to /community/notifications
  • Sending a connection request creates a notification for the recipient
  • Accepting a connection creates a notification for the requester
  • Earning a badge creates a notification
  • Slack DMs are sent alongside in-app notifications (no duplicates)
  • Rate limiter returns 429 when exceeded
  • Working group detail page members link to /community/people/:slug

🤖 Generated with Claude Code

bokelley and others added 9 commits March 2, 2026 20:24
Extract generic notification system from board exploration work. Adds
notifications table, in-app bell icon with dropdown, Slack DM delivery,
and triggers for connection requests/acceptances and badge awards. Also
links working group members to their profiles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix XSS: add proper escapeHtml for all interpolated fields in nav dropdown
- Fix duplicate Slack DMs: remove old notifyConnection* functions, notifyUser
  now handles both in-app and Slack delivery
- Add UUID validation on mark-as-read endpoint (400 instead of 500)
- Clamp negative offset to 0 on list endpoint
- Add rate limiter (120 req/min) on notification endpoints
- Replace hardcoded hex colors with design system CSS variables

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both nav.js and notifications.html used id="notifList", causing
getElementById to find the nav's dropdown element instead of the
page's container. The page's "Loading..." was never replaced.

Renamed page element IDs to pageNotifList, pageMarkAllBtn, and
pageLoadMoreBtn to avoid the collision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire notification system into participation loop:
- Working group join: notify group leaders when someone joins
- Meeting scheduled: notify invited attendees when admin creates meeting
- Event updated: notify registered users when time/venue/status changes
- Event reminder: hourly job sends 24h-before reminders to registrants

Also adds NotificationDatabase.exists() for dedup and
EventsDatabase.getEventsStartingBetween() for the reminder job.

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

Main branch added 259_agent_context_auth_type.sql and
260_org_memberships_created_at_index.sql since our branch was created.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The @adcp/client SDK's OrchestratorOptions.auth type only defines
'bearer', but basic auth support was added in the route handler.
Cast until the SDK type is updated to include basic auth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@bokelley bokelley merged commit 5edd981 into main Mar 4, 2026
6 checks passed
@bokelley bokelley deleted the bokelley/notifications branch March 4, 2026 15:03
bokelley added a commit that referenced this pull request May 1, 2026
The // lgtm[js/request-forgery] suppressions don't actually suppress in
GitHub's CodeQL (LGTM was retired); they only shifted the bytes on the
flagged lines, which made the PR's CodeQL check re-detect known alert
#1316 (already open on main) as "new in this PR".

Reverting makes url-security.ts match main byte-for-byte. The pre-existing
SSRF false-positive remains tracked as alert #1316 on main and should be
dismissed in the security UI as a false positive (validateFetchUrl +
SSRF-safe dispatcher with private-IP rejection at connect time).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley added a commit that referenced this pull request May 2, 2026
…overy (#3690)

* spec: brand_url on get_adcp_capabilities for keys-from-agent-URL discovery

Proposes a top-level brand_url field on get_adcp_capabilities so verifiers
can bootstrap from an agent URL to the operator's brand.json (and from
there to signing keys) without out-of-band knowledge of the operator
domain. Keeps brand.json operator-attested as the trust root — no
jwks_uri added to capabilities.

Verifier algorithm pins agent URL eTLD+1 to brand_url eTLD+1 (with
authorized_operators[] opt-in for SaaS-platform operators), enforces
mandatory identity.key_origins consistency for every declared signing
purpose, and joins the existing request_signature_* error-code family.

Includes a self-hostable AAO reference resolver
(/api/registry/agents/resolve, /api/registry/agents/jwks) framed as a
convenience layer not a trust anchor — separate hostname for the JWKS
endpoint, no stale-while-revalidate on JWKS, native resolution shown
first in caller integration, SDKs default to mode="native".

Spec only — no protocol changes ship in this PR. Reviewed by ad-tech
protocol, security, ad-tech product, and DX experts; their material
feedback is itemized in the spec's "Reviewer feedback addressed" section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* spec: add RFC 7517 use="sig" to JWK examples and require it normatively

Addresses @BaiyuScope3 review on PR #3690 — JWK examples were missing the
RFC 7517 `use` field. Added `use: "sig"` to both example JWKs in the
resolver responses and made it normative: every signing JWK MUST declare
`use: "sig"` for JOSE-library pre-filtering, with the AdCP-specific
purpose granularity riding on `adcp_use`. Verifiers MUST enforce both.
Also clarified that `key_ops` MUST NOT be used together with `use` per
RFC 7517 §4.3 — AdCP standardizes on `use` for cross-library compat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* spec: round-2 fixes + AAO resolver implementation

Spec changes (round 2):
- Reverted "MUST NOT use key_ops with use" (security.mdx:780 requires the
  AdCP four-tuple: use:"sig", key_ops:["verify"], adcp_use, distinct kid).
  JWK examples now match the canonical shape from security.mdx:840.
- Tightened required-when on identity.key_origins to supported_for[]/
  required_for[] non-empty (a no-op declaration with empty arrays
  shouldn't drag in key_origins).
- Step 7 consistency check now skips purposes whose JWKS source was a
  publisher adagents.json signing_keys pin — the pin is an intentional
  override and may legitimately point at a different host.
- Tightened loose SHOULDs to MUSTs (production verifiers MUST use native
  resolution; verifiers MUST honor upstream Cache-Control).
- Dropped /.well-known/adcp-jwks.json shortcut (re-introduced M3-prime
  self-attestation without saving fetches on the trust-required path).
- Expanded SSRF list: pinned PSL snapshot, IPv6 ULA, IPv4-mapped IPv6,
  AWS/GCP/Azure metadata IPs, bracketed-IPv6 zone-ID handling,
  DNS-rebinding ordering, agent_url 2KB cap, per-caller < per-host rate.
- AAO resolver MUST itself enforce required-when (storyboard alone is
  insufficient because mode:"aao" callers would lose H2 protection).
- Added trace[] array + freshness aggregate to /resolve response.
- Added new error code request_signature_key_origin_missing.

AAO resolver implementation:
- server/src/registry/agent-resolver/ with 10 modules: orchestrator,
  capabilities-fetcher (via SingleAgentClient.getAdcpCapabilities),
  brand-json-fetcher, jwks-fetcher (byte-for-byte Cache-Control
  propagation, no SWR), consistency (eTLD+1 binding, key_origins check,
  agent-not-in-brand-json), breadcrumb builder, TTL cache + per-host
  token-bucket rate limiter, SSRF-strict fetch wrapper around safeFetch,
  typed error class, algorithms allowlist.
- Routes /api/registry/agents/resolve and /api/registry/agents/jwks
  wired in server/src/routes/registry-api.ts with OpenAPI registerPath
  declarations + static/openapi/registry.yaml entries.
- JWKS endpoint preserves RFC 7517 purity in the body; trace surfaced
  via X-AAO-Trace-URL header pointing at the resolve endpoint.
- 63 tests passing: 29 consistency + 10 cache + 13 safe-fetch-strict +
  11 integration (in-process express + jose-generated Ed25519 keypair,
  JOSE round-trip verifying a token signed with the fixture key).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(security): suppress CodeQL js/request-forgery false-positive on safeFetch

CodeQL traces user-provided URLs (e.g., the resolver's agent_url query
param) through to the fetch() calls inside safeFetch and flags them as
SSRF, but safeFetch is the function that validates URLs and pins
SSRF-safe dispatch. validateFetchUrl rejects private IPs pre-flight,
buildSsrfSafeDispatcher's lookup callback rejects them at TCP connect
time, redirects re-run validation per hop. Same suppression pattern as
the existing axios.get callers in adagents-manager.ts and brand-manager.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* spec: drop hosted AAO resolver, reframe as @adcp/client SDK + CLI

Round-3 pivot driven by CodeQL flagging the hosted resolver's fetch path
and a fundamental reconsideration: a centralized fetcher of
caller-supplied URLs is the wrong shape for the registry. SSRF
amplification on AAO infrastructure, centralized cache as a single
poisoning point, and "convenience layer that's not a trust anchor"
still drags AAO into JOSE-naive verifiers' trust chains in practice.
Per-process SDK resolution is what the spec already required for
production verifiers; this round commits to it as the only path.

Spec changes:
- Replaced §"Hosted resolver (AAO Registry API)" with §"Client SDK + CLI"
  describing resolveAgent / getAgentJwks / createAgentJwksSet in
  @adcp/client (TS) and adcp (Python), plus npx @adcp/client resolve
  <url> CLI for instant-answer UX.
- Scrubbed AAO-specific surface (aao_signed, X-AAO-* headers,
  cache_until, source: live|cached envelope, separate-hostname
  requirement, mode="aao" SDK option). Trace[]/freshness now ride on
  the SDK return value.
- SSRF list reframed for SDK posture: kept IP blocks (cloud-metadata
  IPs still matter for SDK callers running in cloud workloads),
  dropped per-caller-IP rate limit, reframed per-host rate as
  politeness-not-amplification.
- Documented in-browser playground as the right shape for
  "I have an agent URL, show me its keys" without server-side fetch.
  Added open question on operator CORS for brand.json/JWKS.
- Rollout: dropped PR 4 (hosted resolver). Implementation lands in
  PR 3 (@adcp/client + Python SDK + CLI). Optional follow-up PR for
  the browser playground.

Implementation removed:
- server/src/registry/agent-resolver/ (10 modules)
- server/tests/{unit,integration}/agent-resolver*.test.ts (4 files)
- Two route handlers in server/src/routes/registry-api.ts
- registerPath blocks for both endpoints
- Agent Resolution entries in static/openapi/registry.yaml + the tag
- .changeset/aao-agent-resolver-impl.md

The pure-function modules (consistency, breadcrumb, cache, algorithms,
SSRF-strict fetch wrapper) are the right starting point for the
@adcp/client port; that work happens in a follow-up PR on the
adcp-client repo.

CodeQL suppression on safeFetch kept — documents the existing axios.get
callers' established pattern and stays correct independent of the
resolver routes.

Server unit suite: 2930/2930 passing. Typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(security): revert lgtm comments — modern CodeQL ignores them

The // lgtm[js/request-forgery] suppressions don't actually suppress in
GitHub's CodeQL (LGTM was retired); they only shifted the bytes on the
flagged lines, which made the PR's CodeQL check re-detect known alert
#1316 (already open on main) as "new in this PR".

Reverting makes url-security.ts match main byte-for-byte. The pre-existing
SSRF false-positive remains tracked as alert #1316 on main and should be
dismissed in the security UI as a false positive (validateFetchUrl +
SSRF-safe dispatcher with private-IP rejection at connect time).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(schema): brand_url on get_adcp_capabilities for keys-from-agent-URL discovery

Adds the top-level `brand_url` field on `get_adcp_capabilities` so
verifiers can bootstrap from an agent URL to that agent's signing keys
without out-of-band knowledge of the operator domain. Closes the
discovery gap in the request-signing chain — capabilities → brand_url →
brand.json → agents[] → jwks_uri → JWKS.

Schema (static/schemas/source/protocol/get-adcp-capabilities-response.json):
- New top-level `brand_url` (HTTPS URI).
- Schema-optional in 3.x; storyboard-enforced when the agent declares
  any signing posture (`request_signing.supported_for`/`required_for`
  non-empty, `webhook_signing.supported === true`, or any field under
  `identity.key_origins`). Becomes schema-required in 4.0.
- Distinct from existing `sponsored_intelligence.brand_url`, which
  remains a rendering pointer for SI agent visuals.

Security (docs/building/implementation/security.mdx):
- New §"Discovering an agent's signing keys via brand_url" with the
  8-step verifier algorithm: eTLD+1 origin binding (closes shared-
  tenancy spoofing), `authorized_operators[]` opt-in for SaaS-platform-
  as-operator, byte-equal agents[].url match, mandatory key_origins
  consistency check (purpose-generic, with sell-side webhook carve-out
  for adagents.json signing_keys pin), default jwks_uri at agent origin.
- Eight new request_signature_* rejection codes with detail fields:
  brand_url_missing, capabilities_unreachable, brand_json_unreachable,
  brand_origin_mismatch, agent_not_in_brand_json, brand_json_ambiguous,
  key_origin_mismatch, key_origin_missing.
- Trust-root distinction documented: brand.json operator-attested,
  adagents.json publisher-attested, agent never self-attests.
- Existing JWKS-discovery prose now points at the new section.

Backwards compatibility: strictly additive. Verifiers that ignore
brand_url continue to work. The full design (with reviewer history,
multi-tenant operator handling, SDK + CLI integration, and rejected
alternatives) is in specs/capabilities-brand-url.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(security): address expert-review fixes on brand_url discovery

Round-2 expert review on the actual schema diff (commit 4a7aa5d)
flagged several correctness/security/clarity items in security.mdx and
the schema description. This commit addresses Tier 1 (correctness +
security) and Tier 2 (clarity) items inline:

Correctness / security:
- Heading depth: §"Discovering an agent's signing keys" promoted from
  ##### to #### so it's a sibling of §Agent key publication / §Agent
  identity / §Verifier checklist (not orphaned under Agent key
  publication).
- Step 1: capabilities fetch MUST go through SSRF validation and HTTPS
  enforcement at the verifier level (not just schema-level).
- Step 2: 3.x→4.0 transition qualified by `supported_versions`
  containing any 4.x release — a 4.0 verifier talking to a 3.x-only
  agent MUST continue to accept absent brand_url.
- Step 3: explicit pinned PSL snapshot requirement with publicsuffix.org
  pointer; ICANN+PRIVATE sections both in scope so platforms like
  vercel.app are treated as suffixes. Two verifiers on different
  snapshots are non-conformant.
- Step 4: brand.json fetch MUST NOT follow redirects (the single-redirect
  carve-out for authoritative_location is scoped to that field and MUST
  NOT be inherited). Connect/total/body-cap budgets stated. Cache TTL
  MUST be bounded above by JWKS revocation polling. Negative-cache MUST
  NOT exceed 60s.
- Step 6/7: publisher-pin carve-out is per-(agent, purpose, role) tuple,
  not per-purpose globally. Operator-side use of the same purpose still
  enforced. Closes the operator-side substitution vector.
- SI dual brand_url: explicit MUST rule — verifiers MUST use top-level
  brand_url for key discovery, MUST NOT use sponsored_intelligence.
  brand_url as a trust-root pointer.
- Verifier checklist step 7: explicit cross-reference to the discovery
  preamble as a precondition for keyid resolution. Implementers reading
  the checklist alone won't miss the discovery chain.

Clarity:
- Schema description: trimmed; constraints moved to security.mdx with an
  explicit anchor URL (not a bare § reference). Includes the 4.x
  required-when qualifier and the SI MUST rule.
- Step 5: trailing-slash / scheme-mismatch / IDN-punycode common-cause
  hint on agent_not_in_brand_json (the most common integrator failure).
- Detail field HTML-escape note for counterparty-sourced strings
  (brand_url, matched_entries[]).
- Error-code table: added Remediation column. Ops can act on each code
  without reading prose.

Validation: build:schemas, test:schemas (7), test:json-schema (255),
test:composed (32) all pass.

Reviewed by ad-tech-protocol, security, docs, and dx experts on
commit 4a7aa5d.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(schema): rename brand_url → identity.brand_json_url + x-adcp-validation

Round-3 expert review settled on three structural improvements:

1. **Move into `identity` block.** brand_url was at the top level of
   get_adcp_capabilities; it's now `identity.brand_json_url`,
   co-located with `key_origins`, `per_principal_key_isolation`, and
   `compromise_notification` — the trust-posture fields it's
   load-bearing for. The required-when contract reads naturally now:
   "if any other identity field is set, brand_json_url MUST be set."

2. **Rename `brand_url` → `brand_json_url`.** The naming distinguishes
   from `sponsored_intelligence.brand_url` semantically: `brand_url` is
   reserved for "the brand being advertised" contexts (which SI's
   field is); `brand_json_url` names the file artifact (the operator's
   brand.json), independent of whether the operator is a single brand,
   a house, an agency, or a pure operator record. Defuses the
   name-collision footgun that needed a MUST rule before.

3. **Lift constraints into x-adcp-validation extension.** Required-when
   rules, verifier constraints, and distinct-from relationships now
   live in a structured `x-adcp-validation` keyword on the field. The
   storyboard runner and SDK validators consume the rules
   programmatically; codegen consumers get a tight 2-sentence JSDoc
   instead of a 350-word wall of text. JSON Schema validators ignore
   unknown `x-` keys per draft-07.

Schema:
- `identity.brand_json_url` added inside the existing `identity` block
  with the `x-adcp-validation` extension carrying the structured
  required-when rules and verifier constraints.
- Top-level `brand_url` removed.

Security.mdx:
- §"Discovering an agent's signing keys via brand_json_url" — heading
  and prose updated for the new field name and path.
- New §"Quickstart: implement a brand_json_url-based verifier" mirroring
  §"Quickstart: opt into request signing" — 8 numbered steps + 30-line
  TypeScript pseudocode block. Validates against compliance fixture
  brand.json + JWKS.
- New §"Reference implementations" naming @adcp/client (TS), adcp
  (Python), adcp-go (Go) with their resolveAgent/getAgentJwks/
  verify_request_signature signatures and per-SDK CLI commands.
- Error-code names updated: request_signature_brand_json_url_missing
  (was brand_url_missing); detail field brand_url_etld1 →
  brand_json_url_etld1.

New docs/reference/schema-extensions.mdx:
- Canonical reference for AdCP's x-prefixed schema annotations.
- Documents the existing x-status convention plus the new
  x-adcp-validation keyword (sub-keys, conformance rules, current
  usage).
- Wired into docs.json sidebar in two places.

Spec doc:
- Go SDK signatures added to §SDK API (resolver.ResolveAgent,
  verify.RequestSignature, options shape).
- CLI section names all three SDK CLIs (npx @adcp/client / python -m
  adcp / adcp-go) with identical output format.

Validation: build:schemas, test:schemas (7), test:json-schema (255),
test:composed (32) all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(capabilities): wire training agent to emit identity.brand_json_url + round-4 expert fixes

Training agent at test-agent.adcontextprotocol.org now declares
identity.brand_json_url pointing at https://adcontextprotocol.org/.well-known/brand.json,
and the AdCP-domain brand.json lists the training agent's URL byte-equally
in agents[] with a jwks_uri at the same eTLD+1. End-to-end discovery
chain (capabilities → brand_json_url → brand.json → agents[] → jwks_uri)
runs green via scripts/e2e-resolve-training-agent.ts in both --inproc
and HTTP modes.

Round-4 expert fixes to docs/building/implementation/security.mdx:
- Step 1 is a protocol-level call (MCP tools/call / A2A skill), not raw GET
- Origin comparisons canonicalize ASCII-lowercase + IDNA-2008 A-label
- Budgets/timeouts/redirect bans on every fetch (capabilities, brand.json, JWKS)
- Strict-JSON parse on brand.json + new request_signature_brand_json_malformed code
- PSL pinning called out per language with named libraries
- Step 8 cross-ref off-by-one fixed (verifier checklist step 8+)
- Reference implementations paragraph lists result-shape fields per SDK
- Python/Go CLIs install adcp binary (console_scripts / distinct from module path)
- Error table remediation specifics on retry/cache discipline + concrete URL hint

docs/reference/schema-extensions.mdx: required_when is an object wrapping
any_of/all_of (mirroring JSON Schema's anyOf/allOf precedent), not a bare
array. TODO link replaced with #3827.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: forward-compatibility note — 3.0 implementers can adopt brand_json_url today

Add an explicit guidance callout for 3.0-pinned implementers. AdCP
doesn't backport new schema fields to patch releases, but the wire
shape of identity.brand_json_url is forward-compatible: a 3.0 seller
can populate the field today and a 3.x verifier picks it up
automatically; a 3.0 verifier can read it opportunistically and run
the 8-step chain when present. No version bump, no coordination — the
chain is just HTTPS fetches and JSON parsing.

Lands in two places:
- security.mdx §"Discovering an agent's signing keys" — Info callout
  right after the error-code table, where verifier authors will see it.
- Changeset for the next 3.x minor — generates the release-notes entry
  with the same guidance for adopters reading the changelog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(schema): identity.additionalProperties → true for forward-compat

The storyboard runner CI failed with "/identity: must NOT have
additional properties" when the training agent emitted brand_json_url.
The identity block had additionalProperties: false, which rejects new
fields added in minor releases — including brand_json_url itself when
validated by a runner using a pinned 3.0.x schema.

Flipping to true matches the convention on peer capability blocks
(measurement, conversion_tracking, audience_targeting, content_standards
all have additionalProperties: true). The empty-object semantic from
the description still holds; receivers MUST treat unknown sub-fields as
advisory and not infer capability from them.

Tradeoff: typos in identity sub-field names will silently pass schema
validation. Acceptable because the storyboard runner enforces required
sub-fields per posture (key_origins.{purpose} when signing supported,
etc.) and unknown fields don't trigger capability claims.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(training-agent): defer identity.brand_json_url emit to post-3.1 SDK publish

The storyboard runner uses @adcp/sdk's pinned ADCP_VERSION (currently
'3.0.1') to fetch the response schema from
adcontextprotocol.org/schemas/3.0.1/. That published schema doesn't
include identity.brand_json_url and has identity.additionalProperties:
false, so the runner rejects responses with the new field.

Loosening additionalProperties on the source schema doesn't help here
because the runner reads the published 3.0.1 artifact, not the source
tree. The right fix is a coordinated SDK version bump after 3.1 ships.

For now, drop the training-agent emit. Protocol changes (schema field,
security.mdx algorithm, error taxonomy, schema-extensions doc, spec
archive, changeset) all ship as designed. The brand.json + e2e fixture
wiring stays in place — `scripts/e2e-resolve-training-agent.ts --inproc`
exercises the full chain against an in-process Express stack. Production
re-wiring is a follow-up tracked alongside the SDK version bump.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(e2e): drop dead etldMatch reassignment, prefer const

Address github-code-quality bot finding on
scripts/e2e-resolve-training-agent.ts:291. The reassignment
`etldMatch = true` after a successful authorized_operators[]
delegation check was a useless write — the variable is only read
earlier in the block (trace note + branch condition). Removed the
write and tightened `let` → `const` since the value is now
single-assignment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant