You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of #1029 (linked deployment changes epic — read it first for the big picture). Needs #1032.
Goal
Make the link visible from both ends. The deployment detail page gets a Linked changes panel
(sequence, query type, live status chip, link to /queries/:id, "ready" summary that explains a
held gate); the query detail page gets a Ships in deployment callout listing the deployment
requests that carry it. No new route, no new store.
Today pages/deployments/DeploymentDetailPage.tsx loads the request and gate with TanStack
(:75-92, keys in api/deploymentRequests.ts:11-18) and renders DetailCards at :386-468; pages/queries/QueryDetailPage.tsx loads queryKeys.detail (:90) and stacks Alert banners
(:392-542) above its cards. types/api.ts:4368DeploymentRequest has linked_queries
since #1030; DeploymentRequestListFilters (:4410) has no query filter.
Design constraints agreed on the epic:
"Ships in" is served by GET /deployment-requests?query_request_id= — the list's own
visibility rule applies, so a viewer only sees deployments they could open. QueryDetail gains
no field; core cannot depend on deploygov. The backend filter (DeploymentRequestListFilter
DeploymentRequestSpecifications.forFilter, :25) ships in this issue's PR together with its docs/04-api-spec.md row (:7701), DefaultDeploymentRequestServiceTest and DeploymentRequestControllerTest cases — document the endpoint change before the code.
Status chips through StatusPill / utils/statusColors.ts:153; QueryTypePill for the type;
no hex colours.
src/api/deploymentRequests.ts — pass query_request_id through listDeploymentRequests;
add deploymentKeys.forQuery(queryId) so the query page can invalidate independently. api/deploymentRequests.test.ts covers the new param.
components/deployments/LinkedChangesPanel.tsx (new) — an antd Table inside a DetailCard: # (sequence_order), type (QueryTypePill), datasource, status (StatusPill,
a muted "no longer exists" chip when null), and a Link to /queries/:id. Header shows
"N of M ready" from the gate payload and, when not ready, the pending ids in the same tone as
the frozen banner (deploygov.detail.bannerFrozen). Render nothing when there are no links —
the panel must not add an empty card to every deployment.
DeploymentDetailPage.tsx — mount the panel between the approvals card (:409) and the
metadata card (:441); the releasability banner (bannerReleasable / bannerFrozen) gains a
third state, bannerLinkedPending, when releasable is false only because of links.
components/queries/ShipsInDeploymentCallout.tsx (new) — useQuery on deploymentKeys.forQuery(id) → listDeploymentRequests({ query_request_id: id, size: 20 }), enabled only when the user's org has the deployments governance domain enabled (the same hasPermission / domain gate the sidebar uses for /deployments), rendering an Alert with
one line per deployment: pipeline, environment, version, StatusPill, link to /deployments/:id. Hidden when the list is empty; a 404/403 from a caller without
deployment visibility is rendered as nothing, never as showApiError.
QueryDetailPage.tsx — mount the callout with the other banners (:392-542), after the
SQL-review findings so the reviewer reads "what is it" before "where does it ship".
i18n — every string through t() under deploygov.linked.* and queries.detail.ships_in_*
(snake_case, matching the queries.detail.* convention at en.json), in all seven src/locales/*.json; locales/__tests__/locales.parity.test.ts enforces it. Backend enum
values through src/utils/enumLabels.ts (queryStatusLabel, :81).
e2e — extend e2e/tests/deployment-review.spec.ts (serial block at :38, pipeline and
environment created in beforeAll) with one spec: createPostgresDatasource → submitQueryViaApi (e2e/helpers/datasources.ts:334) with a DDL statement → approveQueryViaApi (:465) → triggerDeploymentViaApi (e2e/helpers/deployments.ts:119,
extended with queryRequestIds?: string[]) → the deployment page shows the panel row with the
query's status chip and the link opens the query page, whose callout names the deployment's
version. Extend getDeploymentGateViaApi (:182) to expose linked_queries_ready and confirmDeploymentExecutionViaApi (:215) with an executeLinkedQueries option. Note: this
requires deploygov: bind deployment environments to datasources and make sort_order meaningful #877's environment datasource_id in createDeploymentEnvironmentViaApi (:48).
Acceptance criteria
Component tests: LinkedChangesPanel (ordered rows, null-status chip, hidden when empty,
"N of M ready"), ShipsInDeploymentCallout (renders per deployment, hidden on empty, hidden on
a visibility error, disabled without the domain), and the new banner state on DeploymentDetailPage.test.tsx; QueryDetailPage.test.tsx mounts the callout.
The e2e spec passes in the main e2e variant (.claude/patterns/e2e-spec.md); selectors use
roles and t()-driven text, no ant-* class names.
Backend: filter unit + controller tests, docs/04 row, MessagesParityTest untouched (no new
backend strings).
npm run lint, npm run typecheck, npm run test:coverage (≥ 90 % lines / ≥ 80 % branches)
and npm run build pass — build is stricter than typecheck (noUncheckedIndexedAccess on
tests too), and one TS error fails all three e2e variants.
help-corpus/ regenerated (node .github/scripts/build-help-corpus.mjs) because en.json
changed; the help-corpus CI job fails on drift.
Part of #1029 (linked deployment changes epic — read it first for the big picture). Needs #1032.
Goal
Make the link visible from both ends. The deployment detail page gets a Linked changes panel
(sequence, query type, live status chip, link to
/queries/:id, "ready" summary that explains aheld gate); the query detail page gets a Ships in deployment callout listing the deployment
requests that carry it. No new route, no new store.
Today
pages/deployments/DeploymentDetailPage.tsxloads the request and gate with TanStack(
:75-92, keys inapi/deploymentRequests.ts:11-18) and rendersDetailCards at:386-468;pages/queries/QueryDetailPage.tsxloadsqueryKeys.detail(:90) and stacksAlertbanners(
:392-542) above its cards.types/api.ts:4368DeploymentRequesthaslinked_queriessince #1030;
DeploymentRequestListFilters(:4410) has no query filter.Design constraints agreed on the epic:
GET /deployment-requests?query_request_id=— the list's ownvisibility rule applies, so a viewer only sees deployments they could open.
QueryDetailgainsno field;
corecannot depend ondeploygov. The backend filter (DeploymentRequestListFilterDeploymentRequestSpecifications.forFilter,:25) ships in this issue's PR together with itsdocs/04-api-spec.mdrow (:7701),DefaultDeploymentRequestServiceTestandDeploymentRequestControllerTestcases — document the endpoint change before the code.StatusPill/utils/statusColors.ts:153;QueryTypePillfor the type;no hex colours.
Steps
src/types/api.ts—DeploymentLinkedQuery { query_request_id; sequence_order; status: QueryStatus | null; query_type: QueryType | null; datasource_id: string | null; datasource_name: string | null };linked_queries_ready: booleanandlinked_queriesonDeploymentGateStatus;query_request_id?: stringonDeploymentRequestListFilters.src/api/deploymentRequests.ts— passquery_request_idthroughlistDeploymentRequests;add
deploymentKeys.forQuery(queryId)so the query page can invalidate independently.api/deploymentRequests.test.tscovers the new param.components/deployments/LinkedChangesPanel.tsx(new) — an antdTableinside aDetailCard:#(sequence_order), type (QueryTypePill), datasource, status (StatusPill,a muted "no longer exists" chip when null), and a
Linkto/queries/:id. Header shows"N of M ready" from the gate payload and, when not ready, the pending ids in the same tone as
the frozen banner (
deploygov.detail.bannerFrozen). Render nothing when there are no links —the panel must not add an empty card to every deployment.
DeploymentDetailPage.tsx— mount the panel between the approvals card (:409) and themetadata card (
:441); the releasability banner (bannerReleasable/bannerFrozen) gains athird state,
bannerLinkedPending, whenreleasableis false only because of links.components/queries/ShipsInDeploymentCallout.tsx(new) —useQueryondeploymentKeys.forQuery(id)→listDeploymentRequests({ query_request_id: id, size: 20 }),enabledonly when the user's org has the deployments governance domain enabled (the samehasPermission/ domain gate the sidebar uses for/deployments), rendering anAlertwithone line per deployment: pipeline, environment, version,
StatusPill, link to/deployments/:id. Hidden when the list is empty; a404/403from a caller withoutdeployment visibility is rendered as nothing, never as
showApiError.QueryDetailPage.tsx— mount the callout with the other banners (:392-542), after theSQL-review findings so the reviewer reads "what is it" before "where does it ship".
t()underdeploygov.linked.*andqueries.detail.ships_in_*(snake_case, matching the
queries.detail.*convention aten.json), in all sevensrc/locales/*.json;locales/__tests__/locales.parity.test.tsenforces it. Backend enumvalues through
src/utils/enumLabels.ts(queryStatusLabel,:81).e2e/tests/deployment-review.spec.ts(serial block at:38, pipeline andenvironment created in
beforeAll) with one spec:createPostgresDatasource→submitQueryViaApi(e2e/helpers/datasources.ts:334) with a DDL statement →approveQueryViaApi(:465) →triggerDeploymentViaApi(e2e/helpers/deployments.ts:119,extended with
queryRequestIds?: string[]) → the deployment page shows the panel row with thequery's status chip and the link opens the query page, whose callout names the deployment's
version. Extend
getDeploymentGateViaApi(:182) to exposelinked_queries_readyandconfirmDeploymentExecutionViaApi(:215) with anexecuteLinkedQueriesoption. Note: thisrequires deploygov: bind deployment environments to datasources and make sort_order meaningful #877's environment
datasource_idincreateDeploymentEnvironmentViaApi(:48).Acceptance criteria
LinkedChangesPanel(ordered rows, null-status chip, hidden when empty,"N of M ready"),
ShipsInDeploymentCallout(renders per deployment, hidden on empty, hidden ona visibility error, disabled without the domain), and the new banner state on
DeploymentDetailPage.test.tsx;QueryDetailPage.test.tsxmounts the callout..claude/patterns/e2e-spec.md); selectors useroles and
t()-driven text, noant-*class names.docs/04row,MessagesParityTestuntouched (no newbackend strings).
npm run lint,npm run typecheck,npm run test:coverage(≥ 90 % lines / ≥ 80 % branches)and
npm run buildpass —buildis stricter thantypecheck(noUncheckedIndexedAccessontests too), and one TS error fails all three e2e variants.
help-corpus/regenerated (node .github/scripts/build-help-corpus.mjs) becauseen.jsonchanged; the
help-corpusCI job fails on drift.