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 #1031.
Goal
Show the deployment analyzer the database changes a release actually ships. The analysis input
gains the linked queries' SQL and their prior AI verdicts, rendered under a separate size cap and
placed in a dedicated section of the deployment prompt. Fail-safe behaviour is unchanged.
Today ai/api/DeploymentAnalyzer.java:34-37 defines DeploymentAnalysisInput(organizationId, aiConfigId, provider, environment, version, commitSha, artifactRef, metadataContext, language); deploygov/internal/DeploymentAnalysisListener.java:70-74 builds it and caps the metadata slice
with MAX_METADATA_CONTEXT_CHARS = 16_000 (:45, renderMetadata:96-104). ai/internal/DefaultDeploymentAnalyzer.java:26-41 frames a %s-formatted text block and hands it
to strategy.analyze(framed, DbType.CUSTOM, null, language, aiConfigId) (:45) — there is no {{placeholder}} template and no SystemPromptRenderer involvement for deployments, so "a new
prompt placeholder" here means a new %s slot in that block. Lines :36-38 already tell the model
to treat "schema or data migrations" as elevated risk, blind.
Design constraints agreed on the epic:
ai imports nothing new. The context is assembled in deploygov through core.api and
passed as a string; ai never learns what a deployment link is. deploygov → ai.api stays the
only arrow (docs/05-backend.md:4326-4334).
A lookup failure degrades to a placeholder line — never a failed analysis, never an exception
out of the listener beyond the existing AiAnalysisException | AiAnalysisParseException catch.
Prior verdicts are context, not authority: the prompt labels them as earlier analyses of the
individual statements; the deployment's own verdict is still the model's.
Steps
ai/api/DeploymentAnalyzer.java — add String linkedChangesContext to DeploymentAnalysisInput (null or blank = none). Record, so the change is a compile-time
fan-out: update DeploymentAnalysisListener, DefaultDeploymentAnalyzerTest, DeploymentAnalysisListenerTest, and any other constructor call (grep -rn "DeploymentAnalysisInput(").
deploygov/internal/DeploymentAnalysisListener.java — static final int MAX_LINKED_CHANGES_CONTEXT_CHARS = 24_000 beside the metadata cap, and a renderLinkedChanges
that, for each link in sequence_order, reads QueryRequestLookupService.findById (SQL, queryType, status, datasourceId) and AiAnalysisLookupService.findByQueryRequestId
(riskLevel, riskScore, summary, failed) and emits one block per link: #<seq> [<queryType>] status=<status> datasource=<name> prior_ai_risk=<LEVEL>/<score> on one
line, the summary on the next, then the SQL — each SQL individually capped so one 50 kB
migration cannot starve the rest, and the whole string truncated with the same "\n… (truncated)" marker renderMetadata uses. A missing analysis renders prior_ai_risk=(none), a failed one (analysis failed), a missing query (query no longer exists). Wrap the whole render in a RuntimeException catch that returns "(linked changes could not be loaded)" and logs at WARN. Both caps are constants (the deploygov: DeploymentReviewEscalationJob, reviewer nudges and DeploygovProperties #985DeploygovProperties can absorb them later if anyone asks).
ai/internal/DefaultDeploymentAnalyzer.java — one new %s section between the release
metadata and the risk guidance: Linked database changes (each is a governed AccessFlow query request; prior_ai_risk is an earlier per-statement analysis, not a verdict on this deployment): followed by blankToNone(input.linkedChangesContext()). Extend the guidance line to say that a linked
change already rated HIGH/CRITICAL, a DDL against a production-like environment, or a deploy
that ships no linked change but whose metadata mentions migrations, is elevated risk. Keep the null schema-context argument and its comment (:42-44) — the linked SQL is already inside framed; passing it twice bills the org twice.
docs/05-backend.md (:4326-4334) and docs/18-deployment-governance.md §3 (:153)
— describe the new input field, both caps, the prompt section and the degradation rules; deploygov: linked-changes documentation sweep #1036 does the wider sweep, but the analyzer contract is documented in this PR.
Acceptance criteria
DefaultDeploymentAnalyzerTest: the framed prompt contains the linked section verbatim when
the context is present and (none) when null/blank; the strategy still receives null schema
context; the rate limiter is still enforced first.
DeploymentAnalysisListenerTest: rendering order follows sequence_order; per-SQL and total
caps truncate with the marker; missing analysis / failed analysis / deleted query render their
placeholders; a RuntimeException from a lookup yields the "could not be loaded" line and the
analysis still completes; a request with no links passes null; the existing skipped / failed
event paths are byte-for-byte unchanged.
mvn -f backend/pom.xml -q test -Dtest='ApplicationModulesTest,ApiPackageDependencyTest'
passes — ai.api has no new import, and ai has no deploygov dependency.
Coverage ≥ 90 % lines / ≥ 80 % branches on the touched classes.
Part of #1029 (linked deployment changes epic — read it first for the big picture). Needs #1031.
Goal
Show the deployment analyzer the database changes a release actually ships. The analysis input
gains the linked queries' SQL and their prior AI verdicts, rendered under a separate size cap and
placed in a dedicated section of the deployment prompt. Fail-safe behaviour is unchanged.
Today
ai/api/DeploymentAnalyzer.java:34-37definesDeploymentAnalysisInput(organizationId, aiConfigId, provider, environment, version, commitSha, artifactRef, metadataContext, language);deploygov/internal/DeploymentAnalysisListener.java:70-74builds it and caps the metadata slicewith
MAX_METADATA_CONTEXT_CHARS = 16_000(:45,renderMetadata:96-104).ai/internal/DefaultDeploymentAnalyzer.java:26-41frames a%s-formatted text block and hands itto
strategy.analyze(framed, DbType.CUSTOM, null, language, aiConfigId)(:45) — there is no{{placeholder}}template and noSystemPromptRendererinvolvement for deployments, so "a newprompt placeholder" here means a new
%sslot in that block. Lines:36-38already tell the modelto treat "schema or data migrations" as elevated risk, blind.
Design constraints agreed on the epic:
aiimports nothing new. The context is assembled indeploygovthroughcore.apiandpassed as a string;
ainever learns what a deployment link is.deploygov → ai.apistays theonly arrow (
docs/05-backend.md:4326-4334).out of the listener beyond the existing
AiAnalysisException | AiAnalysisParseExceptioncatch.individual statements; the deployment's own verdict is still the model's.
Steps
ai/api/DeploymentAnalyzer.java— addString linkedChangesContexttoDeploymentAnalysisInput(null or blank = none). Record, so the change is a compile-timefan-out: update
DeploymentAnalysisListener,DefaultDeploymentAnalyzerTest,DeploymentAnalysisListenerTest, and any other constructor call (grep -rn "DeploymentAnalysisInput(").deploygov/internal/DeploymentAnalysisListener.java—static final int MAX_LINKED_CHANGES_CONTEXT_CHARS = 24_000beside the metadata cap, and arenderLinkedChangesthat, for each link in
sequence_order, readsQueryRequestLookupService.findById(SQL,queryType,status,datasourceId) andAiAnalysisLookupService.findByQueryRequestId(
riskLevel,riskScore,summary,failed) and emits one block per link:#<seq> [<queryType>] status=<status> datasource=<name> prior_ai_risk=<LEVEL>/<score>on oneline, the summary on the next, then the SQL — each SQL individually capped so one 50 kB
migration cannot starve the rest, and the whole string truncated with the same
"\n… (truncated)"markerrenderMetadatauses. A missing analysis rendersprior_ai_risk=(none), a failed one(analysis failed), a missing query(query no longer exists). Wrap the whole render in aRuntimeExceptioncatch that returns"(linked changes could not be loaded)"and logs atWARN. Both caps are constants (thedeploygov: DeploymentReviewEscalationJob, reviewer nudges and DeploygovProperties #985
DeploygovPropertiescan absorb them later if anyone asks).ai/internal/DefaultDeploymentAnalyzer.java— one new%ssection between the releasemetadata and the risk guidance:
Linked database changes (each is a governed AccessFlow query request; prior_ai_risk is an earlier per-statement analysis, not a verdict on this deployment):followed byblankToNone(input.linkedChangesContext()). Extend the guidance line to say that a linkedchange already rated HIGH/CRITICAL, a
DDLagainst a production-like environment, or a deploythat ships no linked change but whose metadata mentions migrations, is elevated risk. Keep the
nullschema-context argument and its comment (:42-44) — the linked SQL is already insideframed; passing it twice bills the org twice.docs/05-backend.md(:4326-4334) anddocs/18-deployment-governance.md§3 (:153)— describe the new input field, both caps, the prompt section and the degradation rules;
deploygov: linked-changes documentation sweep #1036 does the wider sweep, but the analyzer contract is documented in this PR.
Acceptance criteria
DefaultDeploymentAnalyzerTest: the framed prompt contains the linked section verbatim whenthe context is present and
(none)when null/blank; the strategy still receivesnullschemacontext; the rate limiter is still enforced first.
DeploymentAnalysisListenerTest: rendering order followssequence_order; per-SQL and totalcaps truncate with the marker; missing analysis / failed analysis / deleted query render their
placeholders; a
RuntimeExceptionfrom a lookup yields the "could not be loaded" line and theanalysis still completes; a request with no links passes
null; the existing skipped / failedevent paths are byte-for-byte unchanged.
mvn -f backend/pom.xml -q test -Dtest='ApplicationModulesTest,ApiPackageDependencyTest'passes —
ai.apihas no new import, andaihas nodeploygovdependency.