feat(trello): add required label filter for webhook processing#2
feat(trello): add required label filter for webhook processing#2ada-evorada wants to merge 1 commit into
Conversation
CI Failures ResolvedFixes Applied
Root CauseThe Verification
|
suda
left a comment
There was a problem hiding this comment.
Summary
Clean, correct implementation of the required label filter. Logic is placed in the right layer, follows existing patterns throughout, and all CI checks pass.
Code Issues
Should Fix
Unrelated dependency change in the same PR: package.json and package-lock.json include an axios bump (1.13.5 → 1.15.0) and a proxy-from-env major version bump (1.1.0 → 2.1.0), plus a new axios: >=1.15.0 override. This is unrelated to the Trello label filter feature and has no mention in the PR description. It should either be pulled into a separate PR or at minimum documented in the description so reviewers can evaluate it on its own merits.
Minor Observations
-
Filter bypass when
workItemIdis absent: WhenrequiredLabelIdis configured but_event.workItemIdisundefined, the filter is silently skipped and dispatch proceeds. This is intentional defensive behavior (non-card events pass through), but there is no test covering this scenario. A test asserting that dispatch is not skipped (or is skipped with a log) whenworkItemIdis missing would make the intent explicit. -
Error propagation path: If
trelloClient.getCard()throws (transient API failure), the error propagates up towebhook-processor.tswhere it is caught by the existing try/catch and logged as non-fatal. The card already received a 👀 reaction but no ack comment and no job will be queued. This is consistent with the existing error handling pattern across the adapter, so it is not a new problem — just worth knowing as a failure mode.
Everything else looks solid: config propagation is complete across all layers, reducer and buildEditState round-trip correctly, the UI dropdown follows the same FieldMappingRow + manual fallback pattern as the cost field, and the 9 new unit tests cover the meaningful branches.
suda
left a comment
There was a problem hiding this comment.
Despite both cascade and bdgt having set required label in Cascade, moving a Trello card with the label for the bdgt project still was assigned to the cascade project (I removed some sensitive values):
{
"model": {
"id": "69d6891f7325a3eb7e",
"url": "https://trello.com/b/N1R/cascade",
"desc": "",
"name": "Cascade",
"prefs": {
"voting": "disabled",
"canBeOrg": true,
"comments": "members",
"selfJoin": true,
"canInvite": true,
"cardAging": "regular",
"hideVotes": false,
"background": "gradient-bubble",
"cardCounts": false,
"cardCovers": true,
"isTemplate": false,
"autoArchive": null,
"canBePublic": true,
"invitations": "members",
"canBePrivate": true,
"switcherViews": [
{
"enabled": true,
"viewType": "Board"
},
{
"enabled": true,
"viewType": "Table"
},
{
"enabled": false,
"viewType": "Calendar"
},
{
"enabled": false,
"viewType": "Dashboard"
},
{
"enabled": false,
"viewType": "Timeline"
},
{
"enabled": false,
"viewType": "Map"
}
],
"backgroundTile": false,
"backgroundColor": "#DCEAFE",
"backgroundImage": "https://d2k1ftgv7pobq7.cloudfront.net/images/backgrounds/gradients/bubble.svg",
"canBeEnterprise": true,
"permissionLevel": "org",
"sharedSourceUrl": null,
"backgroundTopColor": "#E9F2FE",
"showCompleteStatus": true,
"backgroundDarkColor": "#172F53",
"backgroundDarkImage": "https://d2k1ftgv7pobq7.cloudfront.net/images/backgrounds/gradients/bubble-dark.svg",
"calendarFeedEnabled": false,
"backgroundBrightness": "dark",
"backgroundBottomColor": "#CFE1FD",
"backgroundImageScaled": null,
"hiddenPluginBoardButtons": []
},
"closed": false,
"pinned": false,
"descData": null,
"shortUrl": "https://trello.com/b/NE1R",
"labelNames": {
"red": "Error",
"sky": "project:cascade",
"lime": "",
"pink": "",
"black": "",
"green": "Processed",
"orange": "project:bdgt",
"purple": "Processing",
"yellow": "Auto",
"red_dark": "",
"sky_dark": "",
"blue_dark": "",
"lime_dark": "",
"pink_dark": "",
"red_light": "",
"sky_light": "",
"black_dark": "",
"blue_light": "",
"green_dark": "",
"lime_light": "",
"pink_light": "",
"black_light": "",
"green_light": "",
"orange_dark": "",
"purple_dark": "",
"yellow_dark": "",
"orange_light": "",
"purple_light": "",
"yellow_light": ""
},
"idEnterprise": null,
"idOrganization": "685e770eb2d98a11"
},
"action": {
"id": "69d82b43914131c4a",
"data": {
"old": {
"idList": "69d68913d3c103ebc1"
},
"card": {
"id": "69d82b2306e1456038",
"name": "Add ability to delete uploaded bank statements",
"idList": "69d68925a3d3c103ebc3",
"idShort": 16,
"shortLink": "lm5G4J"
},
"board": {
"id": "69d689125a3d3c103eb7e",
"name": "Cascade",
"shortLink": "NEJV1R"
},
"listAfter": {
"id": "69d6891f7a3d3c103ebc3",
"name": "Planning"
},
"listBefore": {
"id": "69d6891f7325ac103ebc1",
"name": "Backlog"
}
},
"date": "2026-04-09T22:42:11.316Z",
"type": "updateCard",
"limits": null,
"display": {
"entities": {
"card": {
"id": "69d82b2306277c1456038",
"text": "Add ability to delete uploaded bank statements",
"type": "card",
"idList": "69d6891f73a3d3c103ebc3",
"shortLink": "lm554J"
},
"listAfter": {
"id": "69d6891f7323d3c103ebc3",
"text": "Planning",
"type": "list"
},
"listBefore": {
"id": "69d6891f25a3d3c103ebc1",
"text": "Backlog",
"type": "list"
},
"memberCreator": {
}
},
"translationKey": "action_move_card_from_list_to_list"
},
"appCreator": null,
"memberCreator": {
}
},
"webhook": {
"id": "69d768999c617f2a0",
"active": true,
"idModel": "69d6891f7c103eb7e",
"description": "CASCADE webhook",
"consecutiveFailures": 0,
"firstConsecutiveFailDate": null
}
}
suda
left a comment
There was a problem hiding this comment.
Summary
LGTM — the required-label filter is well-designed and well-tested. One naming inconsistency to fix and two undocumented assumptions worth noting.
Code Issues
Should Fix
- src/router/adapters/trello.ts,
dispatchWithCredentialssignature — The first parameter is named_event(underscore prefix = intentionally unused by convention), but the method now actively reads_event.workItemIdin the label-check block. Comparegithub.ts, which names the same parameterevent(no underscore) when it is consumed. Rename toeventfor consistency and to avoid misleading readers.
Comments (no action required)
-
src/router/adapters/trello.ts
parseWebhook— Usesconfig.projects.find()(first match) to evaluateisCardInTriggerListandisReadyToProcessLabelAdded. This is correct when all shared-board projects use identical list IDs and the samereadyToProcesslabel — the expected configuration for the multi-project use case. It would silently drop events if two projects on the same board were configured with differentreadyToProcesslabel IDs. Worth a brief inline comment to document the assumption. -
src/router/webhook-processor.ts, multi-project dispatch loop — The loop breaks on the first non-null dispatch. A project with no
requiredLabelId(catch-all) placed before filtered projects will always win, making subsequent filtered projects unreachable for matching events. This is correct by design, but operators setting up shared-board projects need to know about this ordering constraint. Worth a note in CLAUDE.md or the wizard UI help text.
suda
left a comment
There was a problem hiding this comment.
Unfortunately the run is still being assigned to the wrong project. Interestingly both projects (cascade and bdgt) have the required label set, therefore it I would assume that the incoming webhook would not be routed at all rather than being routed to the wrong project.
I see that the webhook payload from Trello does not pass the labels assigned to the card, just the information about from which and to which board it was moved. It might be necessary to look up the labels first before routing.
|
@suda Thanks for the detailed diagnosis! You're right — the Trello webhook payload for The root issue was architectural: Fix applied:
This ensures the correct project is selected before any dispatch attempt. The |
suda
left a comment
There was a problem hiding this comment.
Summary
The feature is well-structured and the multi-project routing logic is clearly reasoned. However, there is a logic flaw in webhook-processor.ts: returning an empty array from resolveAllProjects (label mismatch — "don't process this event") incorrectly falls through to resolveProject, triggering an extra Trello API call and violating the method's contract. In the happy path this also means a double getCard call per filtered event.
Code Issues
Should Fix
src/router/webhook-processor.ts:337-344 — resolveAllProjects([]) incorrectly falls through to resolveProject
When resolveAllProjects is implemented and returns [] (label mismatch), the fallback treats it identically to "method not implemented":
const projectsToTry: RouterProjectConfig[] = [];
if (adapter.resolveAllProjects) {
const allProjects = await adapter.resolveAllProjects(event);
projectsToTry.push(...allProjects); // pushes nothing when []
}
if (projectsToTry.length === 0) { // true even when resolveAllProjects returned []
const singleProject = await adapter.resolveProject(event); // ← incorrectly called
if (singleProject) projectsToTry.push(singleProject);
}Concrete scenario: project has requiredLabelId, resolveAllProjects returns [] because the card lacks the label → falls through to resolveProject which returns the project by boardId → dispatchWithCredentials is invoked → secondary guard calls checkCardHasRequiredLabel (extra Trello API call) → correctly returns null. Outcome is correct but the path is wrong and wastes an API call. It also logs "No trigger matched" instead of the accurate "No project config found".
The correct branching:
let projectsToTry: RouterProjectConfig[] = [];
if (adapter.resolveAllProjects) {
projectsToTry = await adapter.resolveAllProjects(event);
} else {
const singleProject = await adapter.resolveProject(event);
if (singleProject) projectsToTry = [singleProject];
}There is also no test covering this case: resolveAllProjects returns [] → should short-circuit as "no project config found". The existing test "returns No trigger matched when all projects dispatch null" uses a non-empty array from resolveAllProjects and relies on dispatch returning null — a different path.
src/router/adapters/trello.ts:200-213 — double getCard API call in happy path
When resolveAllProjects successfully pre-filters and returns matched projects, dispatchWithCredentials still unconditionally calls checkCardHasRequiredLabel (a second getCard call). In the standard happy path for a project with requiredLabelId:
resolveAllProjects→getCard(call 1) → returns[project]dispatchWithCredentials→checkCardHasRequiredLabel→getCard(call 2)
The secondary guard is documented as a safety net for when resolveAllProjects errored and returned all candidates unfiltered. However it fires unconditionally in the happy path too, doubling Trello API consumption for every webhook event on label-filtered projects. If the fallback logic in webhook-processor.ts is fixed per above (so resolveAllProjects([]) short-circuits), the guard could be conditioned on whether pre-filtering was definitive.
suda
left a comment
There was a problem hiding this comment.
Summary
The feature is well-designed and thoroughly tested, but there is one blocking bug that will silently prevent the label filter from ever activating in production.
Blocking
requiredLabelId stripped by Zod validation in src/config/schema.ts — feature never activates at runtime
ProjectConfigSchema (lines 56–67 of src/config/schema.ts) defines the Zod schema for the Trello config object but does not include requiredLabelId. Zod strips unknown keys by default in .object().parse(), so the field is silently discarded every time config is loaded from the database.
Runtime path:
DB JSONB (has requiredLabelId)
→ mapProjectRow / buildTrelloConfig ✓ field present
→ validateConfig(rawConfig) ✗ Zod strips unknown field
→ ProjectConfig.trello.requiredLabelId === undefined (always)
Both loadAllProjectConfigs (line 81) and findProjectConfigFromDb (line 104) in configRepository.ts call validateConfig, so every router/worker config load hits this path.
Consequence: project.trello?.requiredLabelId is always undefined at runtime. The pre-filter in resolveAllProjects and the secondary guard in dispatchWithCredentials never activate. Every card is processed regardless of its labels.
The unit tests don't catch this because they mock loadProjectConfig directly, bypassing Zod entirely.
Fix — add one line to src/config/schema.ts inside the trello object schema, after the customFields block:
requiredLabelId: z.string().optional(),
suda
left a comment
There was a problem hiding this comment.
Summary
LGTM — well-designed, correctness-first implementation of label-based routing with solid test coverage across all edge cases.
Architecture & Design
The two-layer defense is appropriate:
- **Pre-filter in ** — one API call before the dispatch loop; correct project(s) selected early
- **Secondary guard in ** — fallback when errored and returned all candidates unfiltered
This avoids redundant calls in the happy path while preserving correctness under API failure. The flag is a reasonable internal coupling mechanism, well-documented with JSDoc.
Code Issues
Nitpick
- In (), the
as stringcast oninitialConfig.requiredLabelIdis redundant — the field is already typed asstring | undefinedonTrelloIntegrationConfig. The?? ''handles the undefined case correctly. Harmless. - Catch-all projects in
resolveAllProjectsare marked_labelPreFiltered: truewhen returned from the catch-all branch, even though they have norequiredLabelIdfilter. This is harmless (the secondary guard short-circuits on falsyrequiredLabelIdregardless), but slightly misleading — it implies label verification occurred when it didn't.
Verified
- All CI checks passing (lint, typecheck, unit + integration tests)
- The
resolveAllProjectsloop correctly uses the first project's credentials to make a singlegetCardcall, then routes all candidates based on those labels — correct since all projects sharing a board ID access the same card data - The 👀 reaction intentionally fires before label filtering (step 5 vs step 6); acknowledged in PR description as per spec
- No-
workItemIdedge case: both the pre-filter and secondary guard skip label checks whenworkItemIdis absent, so a card without a known ID falls through to all candidates — acceptable for events where card ID cannot be determined - Axios upgrade (
>=1.15.0override) is a routine security patch, correct approach
suda
left a comment
There was a problem hiding this comment.
I see now that in the Webhook Log Detail the hook has been assigned to the correct project ID bdgt. However I see the agent failing with:
❌ Error: Error: Integration credential 'scm/implementer_token' not found for project 'cascade'
So somewhere next in the flow the wrong project is being picked up.
suda
left a comment
There was a problem hiding this comment.
Summary
LGTM — the implementation is correct and the defensive two-layer filtering design is sound.
Observations (non-blocking)
on the catch-all path is redundant but harmless
When falls back to catch-all projects (those without a ), it marks them . The secondary guard in checks first, so the flag is never consulted for catch-all projects (they have no label filter). Zero correctness impact, but it slightly overstates what the flag means.
Events without bypass label filtering entirely
If event.workItemId is absent, resolveAllProjects returns all candidates unfiltered, and the secondary guard in dispatchWithCredentials also skips (condition requires event.workItemId). Both projects on a shared board would have triggerRegistry.dispatch called. In practice this shouldn't matter since every processable card event carries a card ID, but it's an edge case worth noting.
Missing test for skipping the secondary guard
The test suite covers dispatching with and without requiredLabelId, and it covers resolveAllProjects pre-filtering, but no test verifies that a project with _labelPreFiltered: true actually skips the checkCardHasRequiredLabel call in dispatchWithCredentials. The optimization works correctly from code inspection, but a test would lock in the behaviour.
👀 reaction fires before label filtering for comment events
Step 5 fires the reaction, label filtering happens at step 7. A comment on a card lacking the required label will receive a 👀 but no agent action. The PR notes this is per-spec, and the reaction is only sent for comment events while label filtering primarily targets card-move/label-add events — so the practical overlap is narrow.
None of these are blocking. All CI checks pass, tests are thorough (9 new test cases for the core filtering logic, 4 for the adapter dispatch paths), and the end-to-end worker path correctly threads projectId through to the PM webhook processor.
96f5136 to
d2014d8
Compare
Adds src/integrations/pm/trello/ with: - manifest.ts: PMProviderManifest wiring TrelloIntegration, TrelloRouterAdapter, all 7 Trello trigger handlers, and TrelloPlatformClient. verifyWebhookSignature wraps the existing verifyTrelloSignature (HMAC-SHA1 of body+callbackUrl) with Host/X-Forwarded-Proto header reconstruction — no shared factory because Trello's signing scheme is unique among providers. - index.ts: side-effect module that calls registerPMProvider(trelloManifest). src/integrations/pm/index.ts: new barrel importing ./trello/index.js for the side effect. Plans 006/3 and 006/4 append jira + linear. Contract adjustments surfaced during TDD: - Dropped parseWebhookPayload field from PMProviderManifest (redundant with routerAdapter.parseWebhook; had wrong return type in 006/1). Each caller uses the appropriate one: router uses routerAdapter, PM-domain code uses pmIntegration.parseWebhookPayload. - Relaxed conformance harness's platform-client assertion from 3 methods to 2 (postComment + deleteComment). updateComment/postReaction are provider extensions, not contract. - registerTestProvider is now additive (no longer resets the registry), so the conformance harness iterates TestProvider AND every real provider side-by-side — validating AC #2 of the spec. Tests: 15 new Trello manifest tests + conformance now 22 (11 per provider x 2). Trello's legacy registrations (bootstrap.ts, builtins.ts, worker-env extractor branch, pm-wizard.tsx branch) still fire — removed in task 3 of this plan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes plan 006/5 — the final cleanup plan of spec 006. Backend: - src/integrations/bootstrap.ts — DELETED. PM registrations flow through src/integrations/pm/index.ts (which also mirrors manifests into integrationRegistry). SCM (GitHub) and alerting (Sentry) self-register via new side-effect modules. - src/github/register.ts + src/sentry/register.ts — new minimal side-effect modules that replace the respective branches of the deleted bootstrap. SCM + alerting stay on the legacy IntegrationModule pattern (out of spec 006 scope). - src/pm/registry.ts — converted to a read-only adapter over pmProviderRegistry. get/getOrNull/all/createProvider/ resolveLifecycleConfig all delegate to the manifest registry. register() is a deprecation warn. The ~9 unmigrated call sites (webhook handlers, manual runner, credential scope, lifecycle, GitHub adapter) keep working unchanged — they now transparently read from the manifest registry, so there is no divergent registration. - src/router/index.ts + src/worker-entry.ts — updated to import the three new side-effect modules instead of the deleted bootstrap. - src/integrations/registry.ts — header comment updated to reflect the new population topology. Tests: - tests/unit/integrations/bootstrap.test.ts — rewritten to cover the new side-effect-module wiring (the file name stays for audit clarity; its content asserts the same end-state invariants). - 8 other test files that imported bootstrap.js for side effect are migrated to import the three new modules (pm barrel + github/register + sentry/register). Docs: - src/integrations/README.md — rewritten. Transitional note + "Legacy path" section removed. The README is now the single canonical author's guide for adding a new PM provider. - CLAUDE.md — integration-abstraction pointer updated to final state. - CHANGELOG.md — entry per plan. Plan-divergence: - AC #2 (delete pm/registry.ts) — became: convert to a read-only delegate. Deleting it would require migrating 9 call sites that are out of spec scope. The delegate preserves the end state (single source of truth = pmProviderRegistry) without downstream churn. - AC #5 (consolidate createXxxLabel tRPC endpoints) — deferred to a follow-up PR. Purely additive cleanup; not required for any spec AC. Both divergences documented in the .done plan. Tests: 7809/7809 pass. Build + lint + typecheck clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tructured envelope, --comment alias) (mongrel-intelligence#1190) * docs(014): spec + plans for cascade-tools agent ergonomics Adds docs/specs/014-cascade-tools-agent-ergonomics.md plus two plans covering shared-infra and create-pr-review adoption. Prompted by prod run 5d993b04-6e05-4ae1-b7de-8c274cf3496b. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(plan-014): lock plan 1 (shared-infra) * feat(cascade-tools): plan 014/1 shared-infra — truthful prompts + envelope Ships the root-cause fix for prod run 5d993b04-6e05-4ae1-b7de-8c274cf3496b plus the shared infrastructure every future gadget inherits: - System-prompt renderer (src/backends/shared/nativeToolPrompts.ts) stops stripping trailing 's' from array param names and claiming '<string> (repeatable)' for every array. Array-of-object params now render as `--<flag> '<json>'` with aliases appended via `|` and a one-line runnable example from the tool definition. - Factory (src/gadgets/shared/cliCommandFactory.ts) gains oclif flag aliases, JSON parsing for array-of-object flags, file-input JSON parsing, `examples` wired into oclif `--help`, and Levenshtein-based 'did you mean' suggestions for mistyped flags (via fastest-levenshtein). - New shared error envelope (src/gadgets/shared/errorEnvelope.ts) — every CLI failure emits `{"success":false,"error":{type,flag?,message,got?, expected?,hint?,example?}}` on stdout plus a one-line prose summary on stderr. All prior `this.error()` / flat `{success:false,error:"<string>"}` call sites migrated. - Contracts widened: ParameterDefinition gains `cliAliases`, FileInput- Alternative gains `parseAs`, ToolManifest parameters carry `items`, `aliases`, `example`. - Manifest generator threads the new fields through. - bin/cascade-tools.js wraps `run()` to swallow oclif ExitError cleanly so the envelope isn't obscured by Node's default stack dump. Plan-1 ACs #1–mongrel-intelligence#17 all delivered. 8438/8438 unit tests passing. Test surface delta: 57 new unit tests across errorEnvelope.test.ts, shared-nativeToolPrompts.test.ts, and factories.test.ts. Seven legacy assertions encoding the pre-014 error surface updated in cli/cli-command- factory, cli/file-input-flags, cli/scm/create-pr-sidecar, cli/scm/create- pr-review-sidecar, backends/claude-code. Plan 2 adopts the pattern on createPRReviewDef — zero shared-file edits — proving the declarative-metadata invariant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(plan-014): lock plan 2 (createprreview-adopt) * feat(cascade-tools): plan 014/2 createprreview-adopt + spec done Applies the spec-014 declarative-metadata pattern to createPRReviewDef: - --comment alias for --comments (the exact muscle-memory mistake from prod run 5d993b04-6e05-4ae1-b7de-8c274cf3496b). - --comments-file <path> (and - for stdin) JSON-parsed escape hatch for long payloads that don't survive shell quoting. - Two declarative fields on createPRReviewDef.parameters.comments.cliAliases + createPRReviewDef.cli.fileInputAlternatives. Zero edits to shared infrastructure (cliCommandFactory, manifestGenerator, nativeToolPrompts, errorEnvelope) — proves spec 014's single-entrypoint invariant. Per-plan ACs #1, #2, #3, #5, #6, mongrel-intelligence#7, mongrel-intelligence#8, mongrel-intelligence#9, mongrel-intelligence#11, mongrel-intelligence#12 auto-verified (unit tests + build + lint + typecheck). AC #4 (binary-level smoke) tagged [manual] because vitest fork-pool workers fail to capture stdout/stderr from spawned binaries that do top-level await import(); the six scenarios were verified manually against the built binary and the trace is recorded in the plan. AC mongrel-intelligence#10 n/a — integration test path abandoned for the same reason. All plans done. Spec 014 marked .done (docs/specs/014-*.md → .done). CHANGELOG Unreleased updated with a per-plan entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-intelligence#1261) * docs(spec): 018 alerting agent + worker boot-failure visibility + plans Spec captures two related gaps surfaced by the first sentry-bound agent run (2026-05-06): alerting agent's missing prompt template and the silent worker boot-failure path that masked it. Two plans downstream: alerting-prompt (feature) and boot-failure-visibility (hardening, follow-on to spec 017). Linear DAG, plan 1 → plan 2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(plan): 018/1 lock Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(agents): alerting agent system prompt template (spec 018, plan 1) Author the system prompt template that the alerting agent's worker tries to load at boot. Every other piece of the alerting agent — YAML definition, capabilities, trigger handlers, context pipeline, Sentry integration — was already wired. Missing the .eta produced an ENOENT at agent boot when the first prod-traffic Sentry alert arrived (cascade project, 2026-05-06). After this plan, an alerting agent dispatched via existing trigger handlers reaches its execution phase end-to-end. The prompt structures the agent's behavior as a three-phase investigator (parse pre-loaded event → confirm root cause via source reads → file or comment) with an explicit INVESTIGATE-AND-FILE-ONLY guardrail. Predictable output structure: 'Investigate: <ErrorType> in <Function> (<file>:<line>)' title and a 4-6 sentence + bullets description. The agent's read-only guarantee is enforced statically by the YAML's capability declaration (no fs:write, no scm:*) — the resolved gadget allowlist excludes WriteFile, CreatePR, CreatePRReview. Plan task #2 was downgraded from a heavy E2E integration test to a static capability-allowlist test (more reliable than behavioral negative-assertions that depend on LLM cooperation); divergence noted in the plan itself. Tests: 10 new in tests/unit/agents/prompts.test.ts; 4 new in tests/unit/agents/definitions/alerting.yaml.test.ts. All green; full unit suite (8808 tests) green; lint + typecheck clean. Spec ACs 1-6 satisfied (full). Marks plan 018/1 as .done. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rebased onto claude/cranky-johnson (upstream/dev + GitLab SCM provider). Consolidates the trello-required-label-filter feature branch and its iterative WIP fix commits into one commit on the new base. The duplicate GitLab commit and the redundant test-fix/axios commits are dropped (already present on cranky-johnson); the projectId-threading fix is also redundant (upstream already threads `preferredProjectId`). When a Trello project sets `requiredLabelId`, only cards carrying that label trigger CASCADE; others are skipped. Supports shared boards where multiple projects map to the same board, distinguished by required label. - Config: `requiredLabelId` added to the shared `trelloConfigSchema`, RouterProjectConfig, configMapper, and pm/config so it survives DB round-trips and reaches the router. - Routing: `resolveAllProjects` pre-filters candidate projects by the card's labels (one Trello API lookup), marking matches `_labelPreFiltered`; `dispatchWithCredentials` keeps a secondary label guard (skipped when pre-filtered) and still wraps dispatch in upstream's `withPMScopeForDispatch`. - webhook-processor iterates all matching projects, dispatching to the first that returns a result, then delegates to `handleTriggerOutcome`. - Wizard: `trelloRequiredLabelId` wired through the aggregate WizardState, the trello provider state slice, and the provider's buildIntegrationConfig / buildEditState. The Label Mapping step composes a required-label picker (board labels with a manual-ID fallback) onto the shared component. - Tests updated for the new state field and multi-project routing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
e6babc9 to
56611d6
Compare
|
Error: Claude Code returned an error result: Failed to authenticate. API Error: 401 terminated Manual intervention may be required. |
Summary
requiredLabelIdconfig field to Trello integration that restricts webhook processing to cards carrying a specific Trello labelTrelloRouterAdapter.dispatchWithCredentials(within credential scope, before ack comments are posted)TrelloFieldMappingStepwizard UIDetails
Config layer
requiredLabelId?: stringtoTrelloConfig(src/pm/config.ts),TrelloIntegrationConfig(src/db/repositories/configMapper.ts), andProjectConfigRaw['trello']requiredLabelIdthroughbuildTrelloConfigandRouterProjectConfig.trelloBackend filtering
checkCardHasRequiredLabel(cardId, requiredLabelId)helper inrouter/trello.ts: returnstruewhen no filter is set, or checks card labels viatrelloClient.getCard()API callwithTrelloCredentialscallback indispatchWithCredentials— only fires whenrequiredLabelIdis configured ANDworkItemIdis known. If card lacks the label, logs atinfolevel and returnsnullFrontend wizard
trelloRequiredLabelIdfield toWizardState,WizardAction, initial state, board reset, and edit state restore (buildEditState)SET_TRELLO_REQUIRED_LABELreducer caseTrelloFieldMappingStepwith help text, below the Cost field, following the same searchable dropdown + manual fallback patternrequiredLabelIdinuseSaveMutationconfig (only when non-empty)Unit tests
tests/unit/router/trello.test.ts: 5 new tests forcheckCardHasRequiredLabeltests/unit/router/adapters/trello.test.ts: 4 new tests for label filtering indispatchWithCredentialsgitlabOnly, credential-scoping test not clearingGITHUB_TOKEN_IMPLEMENTER/GITLAB_TOKEN_IMPLEMENTER)Notes
requiredLabelIdis configured (opt-in, zero cost otherwise)dispatchWithCredentials, so filtered cards still get the emoji (per spec)Trello card: https://trello.com/c/iELcotDw/11-in-the-trello-integration-configuration-under-field-mappings-custom-field-add-an-option-for-required-label-that-can-be-left-blan
🤖 Generated with Claude Code