Deepen PracticeOrdered learning track

Learn Ai Coding Agent Part 043 Java Case Study Dependency Upgrade

9 min read1713 words
PrevNext
Lesson 4364 lesson track36–53 Deepen Practice

title: Learn AI Coding Agent From Scratch - Part 043 description: Case study production-grade untuk Maven dependency upgrade dengan breaking change menggunakan Honk-like AI coding agent, dari dependency graph, risk classification, patch strategy, verifier, agentic repair, hingga PR evidence. series: learn-ai-coding-agent seriesTitle: Learn AI Coding Agent From Scratch order: 43 partTitle: Java Case Study Dependency Upgrade slug: java-case-study-dependency-upgrade tags:

  • ai-coding-agent
  • java
  • maven
  • dependency-upgrade
  • breaking-change
  • verifier
  • supply-chain
  • pull-request date: 2026-07-04

Part 043 — Java Case Study: Maven Dependency Upgrade dengan Breaking Change

Dependency upgrade terlihat sederhana dari luar: ubah angka versi, jalankan test, buat PR.

Untuk manusia senior, itu jelas tidak cukup. Untuk AI coding agent, itu bahkan berbahaya.

Dependency upgrade adalah perubahan supply-chain + perubahan API + perubahan runtime behavior. Kadang diff-nya hanya satu baris di pom.xml, tetapi dampaknya bisa masuk ke transitive dependency, classpath conflict, serialization behavior, security posture, startup behavior, memory footprint, dan compatibility dengan framework lain.

Di part ini kita membangun pipeline Honk-like untuk dependency upgrade Java/Maven yang production-grade. Bukan sekadar:

<version>1.2.3</version>

menjadi:

<version>2.0.0</version>

Target kita adalah agent yang bisa:

  • memahami bagaimana dependency dinyatakan di Maven,
  • membedakan direct dependency, transitive dependency, parent, property, BOM, dan dependency management,
  • mengklasifikasikan risiko upgrade,
  • menjalankan baseline verifier sebelum perubahan,
  • membuat patch minimal,
  • memperbaiki compile error akibat breaking change,
  • tidak menyembunyikan risiko transitive dependency,
  • menghasilkan PR evidence yang bisa dipercaya reviewer.

1. Mental model: dependency upgrade bukan “version bump”

Dependency upgrade memiliki empat layer dampak.

Agent yang hanya mengedit versi tidak sedang melakukan engineering. Ia hanya melakukan mutation.

Agent yang baik memperlakukan dependency upgrade sebagai controlled graph transition:

DependencyGraph(base) + UpgradeRequest + Policy
  -> DependencyGraph(candidate)
  -> GraphDiff
  -> SourceImpact
  -> VerificationReport
  -> RiskVerdict
  -> PR

Ingat invariant utama:

Agent boleh mengubah dependency hanya jika perubahan graph, source impact, dan verification evidence dapat dijelaskan.


2. Scope case study

Kita gunakan contoh fiktif tetapi realistis:

Project:
  payment-orchestrator

Build:
  Maven multi-module
  Java 17
  JUnit 5
  Spring-like application, tetapi case study tidak bergantung pada Spring

Upgrade request:
  Upgrade com.acme:fraud-client from 1.8.7 to 2.1.0

Known breaking changes:
  - FraudClient.check(TransactionRequest) diganti menjadi FraudGateway.assess(FraudAssessmentRequest)
  - FraudResult.getScore() berubah dari int 0..100 menjadi RiskScore value object
  - FraudResult.isBlocked() diganti menjadi decision() == Decision.BLOCK
  - Timeout default berubah dari 500ms ke 2s
  - Package com.acme.fraud.client.* pindah ke com.acme.fraud.api.*

Ini bukan “toy rename”. Perubahan menyentuh:

  • Maven metadata,
  • import,
  • object construction,
  • return type handling,
  • test fixture,
  • default behavior,
  • config.

Kita akan membangun pipeline agent untuk menyelesaikan upgrade ini secara bertahap.


3. Hal yang tidak boleh diasumsikan agent

AI coding agent sering gagal di dependency upgrade karena membuat asumsi yang kelihatan masuk akal tetapi salah.

Asumsi lemahKenapa berbahaya
SemVer major pasti breaking, minor pasti amanBanyak library tidak mematuhi SemVer sempurna. Bahkan patch release bisa mengubah behavior.
Compile hijau berarti amanRuntime behavior, serialization, config default, dan transitive conflict bisa berubah.
Direct dependency adalah satu-satunya dependency yang pentingTransitive dependency dapat berubah dan memengaruhi classpath.
Release notes cukupRelease notes bisa tidak lengkap; verifier tetap wajib.
Test existing cukupTest coverage mungkin tidak menyentuh affected path.
Agent boleh upgrade banyak dependency sekaligusBlast radius membesar dan root cause sulit dibuktikan.
Jika dependency tree lebih baru, berarti lebih baikVersi baru bisa membawa incompatibility, license change, atau vulnerability baru.

Agent harus mulai dari sikap defensif:

Every dependency upgrade is guilty until verified.

4. Maven dependency resolution yang perlu dipahami agent

Kita tidak akan mengulang Maven basic. Kita hanya ambil bagian yang penting untuk agent.

Maven dapat menarik dependency transitif: dependency dari dependency juga masuk ke project. Maven juga memiliki mekanisme mediasi ketika ada beberapa versi artifact yang sama di graph. Karena itu, perubahan satu versi dapat mengubah banyak artifact di effective dependency graph.

Ada beberapa lokasi versi bisa berasal:

pom.xml direct dependency
pom.xml property
parent pom
imported BOM
<dependencyManagement>
plugin dependency
profile-specific dependency
module-specific dependency

Agent harus membedakan where version is declared dan where dependency is used.

Contoh:

<properties>
  <fraud-client.version>1.8.7</fraud-client.version>
</properties>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.acme</groupId>
      <artifactId>fraud-client</artifactId>
      <version>${fraud-client.version}</version>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>com.acme</groupId>
    <artifactId>fraud-client</artifactId>
  </dependency>
</dependencies>

Jika agent mengubah module-level dependency yang tidak punya <version>, patch-nya salah. Ia harus mengubah property atau BOM/parent yang benar.


5. Domain model untuk dependency upgrade

Tambahkan domain object khusus. Jangan menaruh semua ini sebagai string di prompt.

public record DependencyCoordinate(
    String groupId,
    String artifactId,
    String version,
    String classifier,
    String type,
    String scope
) {}

public record UpgradeRequest(
    String repositoryId,
    String baseRef,
    DependencyCoordinate from,
    DependencyCoordinate to,
    UpgradeReason reason,
    UpgradeRiskMode riskMode,
    List<String> allowedModules,
    List<String> forbiddenPaths,
    VerificationProfile verificationProfile
) {}

public enum UpgradeReason {
    SECURITY_FIX,
    PLATFORM_STANDARDIZATION,
    FEATURE_REQUIRED,
    END_OF_LIFE,
    FRAMEWORK_MIGRATION,
    MANUAL_REQUEST
}

public enum UpgradeRiskMode {
    PATCH_ONLY,
    MINOR_ALLOWED,
    MAJOR_WITH_REPAIR,
    SECURITY_EMERGENCY,
    ANALYSIS_ONLY
}

Lalu graph-level artifact:

public record DependencyGraphSnapshot(
    String repoId,
    String commitSha,
    String module,
    List<ResolvedDependency> resolvedDependencies,
    List<DependencyDeclaration> declarations,
    List<BomImport> bomImports,
    List<ParentPom> parents,
    String rawTreeArtifactUri
) {}

public record DependencyGraphDiff(
    DependencyGraphSnapshot before,
    DependencyGraphSnapshot after,
    List<DependencyVersionChange> versionChanges,
    List<ResolvedDependency> added,
    List<ResolvedDependency> removed,
    List<PotentialConflict> conflicts
) {}

Dan source impact:

public record SourceImpactReport(
    List<SymbolReference> directApiReferences,
    List<CompilationError> compileErrorsAfterUpgrade,
    List<TestFailure> testFailuresAfterUpgrade,
    List<ConfigImpact> configImpacts,
    List<RiskNote> unresolvedRisks
) {}

Dengan model ini, agent tidak sekadar bilang “I upgraded dependency”. Agent bisa menjawab:

Dependency declaration changed at root pom property fraud-client.version.
Resolved graph changed in modules payment-core and payment-service.
17 source references were impacted.
13 repaired deterministically/agentically.
4 were skipped because they belong to forbidden module legacy-adapter.
Compile passed. Unit tests passed. Integration tests not run by policy.

6. Pipeline end-to-end

Perhatikan dua baseline:

  1. Baseline before change: memastikan repo memang sehat sebelum agent bekerja.
  2. Candidate after change: memastikan perubahan agent tidak merusak build.

Tanpa baseline, agent bisa dituduh merusak build yang sebenarnya sudah rusak dari awal.


7. Step 1 — Policy gate

Upgrade request harus dicek sebelum sandbox dibuat.

policy:
  dependencyUpgrade:
    maxDirectDependenciesPerRun: 1
    allowMajorUpgradeWithoutApproval: false
    requireBaselineVerification: true
    requireDependencyGraphDiff: true
    requireLicenseScan: true
    requireVulnerabilityScan: true
    allowedScopes:
      - compile
      - runtime
      - test
    forbiddenArtifacts:
      - groupId: "javax.servlet"
        artifactId: "javax.servlet-api"
        reason: "managed by platform"

Policy gate menghasilkan keputusan:

APPROVED_FOR_ANALYSIS
APPROVED_FOR_PATCH
REQUIRES_APPROVAL
BLOCKED

Untuk major upgrade, default sehat adalah:

APPROVED_FOR_ANALYSIS -> human approval -> APPROVED_FOR_PATCH

Honk-like agent bisa sangat produktif, tetapi authority tetap harus jelas.


8. Step 2 — Baseline verifier

Sebelum versi diubah, jalankan baseline.

Minimal:

mvn -q -DskipTests compile
mvn -q test
mvn -q dependency:tree -DoutputFile=target/agent/dependency-tree-before.txt

Untuk multi-module besar, jangan selalu jalankan semua test secara buta. Gunakan profile.

verificationProfile:
  baseline:
    commands:
      - name: compile-all
        argv: ["mvn", "-q", "-DskipTests", "compile"]
        timeoutSeconds: 900
      - name: unit-test-affected-later
        strategy: defer
      - name: dependency-tree
        argv: ["mvn", "-q", "dependency:tree", "-DoutputFile=target/agent/dependency-tree-before.txt"]
        timeoutSeconds: 300

Baseline report harus eksplisit:

{
  "baselineStatus": "PASSED",
  "commitSha": "abc123",
  "commands": [
    { "name": "compile-all", "status": "PASSED", "durationMs": 412000 },
    { "name": "dependency-tree", "status": "PASSED", "artifact": "artifact://..." }
  ]
}

Jika baseline gagal, agent tidak boleh langsung memperbaiki semuanya. Ia harus mengklasifikasikan:

BASELINE_FAILED_UNRELATED
BASELINE_FAILED_RELATED_TO_TARGET
BASELINE_FAILED_INFRA

Untuk upgrade dependency, baseline gagal biasanya berarti run harus berhenti atau turun menjadi analysis-only.


9. Step 3 — Locate version declaration

Agent harus menemukan lokasi perubahan yang benar.

Urutan pencarian:

1. direct dependency version in module pom
2. property used by dependency version
3. dependencyManagement in parent/root pom
4. imported BOM version
5. parent POM version
6. profile-specific declaration
7. plugin dependency declaration

Representasikan hasilnya:

{
  "coordinate": "com.acme:fraud-client",
  "effectiveVersion": "1.8.7",
  "declarationKind": "PROPERTY",
  "file": "pom.xml",
  "propertyName": "fraud-client.version",
  "currentValue": "1.8.7",
  "candidateValue": "2.1.0",
  "affectedModules": ["payment-core", "payment-service"]
}

Jangan edit XML dengan regex rapuh. Gunakan XML parser yang preserve formatting bila memungkinkan. Kalau harus patch teks, gunakan range spesifik dan verify hasilnya dengan Maven effective model.

Patch minimal:

diff --git a/pom.xml b/pom.xml
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
   <properties>
     <java.version>17</java.version>
-    <fraud-client.version>1.8.7</fraud-client.version>
+    <fraud-client.version>2.1.0</fraud-client.version>
   </properties>

Setelah patch, jangan percaya file. Jalankan resolve.


10. Step 4 — Candidate dependency graph

Setelah versi berubah:

mvn -q -DskipTests compile dependency:tree -DoutputFile=target/agent/dependency-tree-after.txt

Graph diff harus menjawab:

Apa dependency langsung yang berubah?
Apa transitive dependency yang ikut berubah?
Apakah ada artifact baru?
Apakah ada artifact hilang?
Apakah ada versi turun/downgrade?
Apakah ada scope berubah?
Apakah ada duplicate class risk?
Apakah ada dependency conflict yang sebelumnya tidak ada?

Contoh report:

{
  "directChange": {
    "coordinate": "com.acme:fraud-client",
    "from": "1.8.7",
    "to": "2.1.0"
  },
  "transitiveChanges": [
    {
      "coordinate": "com.acme:fraud-model",
      "from": "1.3.0",
      "to": "2.0.1",
      "risk": "MAJOR_VERSION_CHANGE"
    },
    {
      "coordinate": "com.fasterxml.jackson.core:jackson-databind",
      "from": "2.14.2",
      "to": "2.15.4",
      "risk": "MINOR_VERSION_CHANGE"
    }
  ],
  "removed": [],
  "added": [
    {
      "coordinate": "com.acme:fraud-spi:2.1.0",
      "scope": "compile"
    }
  ]
}

Graph diff adalah salah satu evidence paling penting di PR.


11. Step 5 — Risk classification

Agent menilai upgrade dari beberapa dimensi.

DimensionSignalRisk
Version delta1.8.7 -> 2.1.0Major upgrade, high risk
Scopecompile/runtimeHigher than test-only
Transitive deltamajor transitive changesHigher risk
Source referencesmany call sitesHigher risk
Public API exposuredependency types leak into public APIHigher risk
Runtime config changetimeout default changedHigher risk
Security reasonCVE fixMay justify faster rollout
Test coverageaffected path untestedHigher risk

Risk output:

{
  "riskLevel": "HIGH",
  "reasons": [
    "Direct dependency major version upgrade",
    "Public API source references found",
    "Default timeout behavior changed",
    "Transitive major upgrade com.acme:fraud-model 1.x -> 2.x"
  ],
  "allowedMode": "SUPERVISED_PR",
  "requiresHumanReview": true
}

Risk classification tidak menghentikan agent secara otomatis. Ia menentukan mode operasi.

LOW       -> autonomous PR possible
MEDIUM    -> supervised PR, strict verifier
HIGH      -> draft PR, human review mandatory
CRITICAL  -> analysis-only unless explicit approval

12. Step 6 — Source impact scan

Setelah graph berubah atau sebelum compile repair, scan source.

Pakai kombinasi:

lexical search     -> cepat, recall tinggi
symbol index       -> import/type/method reference
compiler errors    -> authoritative untuk source incompatibility
test failure       -> behavioral signal

Query contoh:

com.acme.fraud.client
FraudClient
FraudResult
getScore(
isBlocked(
TransactionRequest

Report:

{
  "directApiReferences": [
    {
      "file": "payment-core/src/main/java/acme/payment/FraudCheckService.java",
      "line": 17,
      "symbol": "FraudClient",
      "kind": "TYPE_REFERENCE"
    },
    {
      "file": "payment-core/src/main/java/acme/payment/FraudCheckService.java",
      "line": 44,
      "symbol": "FraudResult.isBlocked",
      "kind": "METHOD_CALL"
    }
  ],
  "testReferences": [
    {
      "file": "payment-core/src/test/java/acme/payment/FraudCheckServiceTest.java",
      "symbol": "FraudClient"
    }
  ]
}

Compiler errors kemudian menjadi repair queue.


13. Step 7 — Compile after version bump

Jalankan compile setelah versi berubah.

Error contoh:

[ERROR] FraudCheckService.java:[12,28] package com.acme.fraud.client does not exist
[ERROR] FraudCheckService.java:[21,19] cannot find symbol: class FraudClient
[ERROR] FraudCheckService.java:[44,21] cannot find symbol: method isBlocked()
[ERROR] FraudCheckService.java:[45,28] bad operand types for binary operator '>='
  first type:  RiskScore
  second type: int

Jangan kirim full log mentah ke model. Buat structured compile error.

{
  "errors": [
    {
      "file": "payment-core/src/main/java/acme/payment/FraudCheckService.java",
      "line": 12,
      "type": "MISSING_PACKAGE",
      "message": "package com.acme.fraud.client does not exist",
      "likelyCause": "Package renamed in fraud-client 2.x"
    },
    {
      "file": "payment-core/src/main/java/acme/payment/FraudCheckService.java",
      "line": 44,
      "type": "MISSING_METHOD",
      "symbol": "FraudResult.isBlocked",
      "likelyReplacement": "FraudResult.decision() == Decision.BLOCK"
    }
  ]
}

Structured error membuat agent lebih presisi.


14. Step 8 — Migration knowledge

Agent butuh knowledge tentang breaking change. Ada beberapa sumber:

1. release notes/changelog
2. migration guide
3. generated compile errors
4. source code/javadoc dari dependency bila tersedia
5. examples/test fixtures
6. manually supplied prompt contract

Untuk platform internal, kita sebaiknya membuat migration contract eksplisit:

migrationContract:
  dependency:
    groupId: com.acme
    artifactId: fraud-client
    fromRange: "[1.0.0,2.0.0)"
    toRange: "[2.0.0,3.0.0)"

  rules:
    - id: package-rename
      from: "com.acme.fraud.client"
      to: "com.acme.fraud.api"
      type: import-rewrite

    - id: client-type
      from: "FraudClient"
      to: "FraudGateway"
      type: type-rewrite
      notes: "Constructor/injection may differ by module. Prefer existing bean if available."

    - id: blocked-decision
      from: "result.isBlocked()"
      to: "result.decision() == Decision.BLOCK"
      type: expression-rewrite

    - id: risk-score
      from: "result.getScore() >= N"
      to: "result.score().value() >= N"
      type: expression-rewrite
      guard: "Only if value() returns same scale 0..100. Otherwise ask for review."

  configChanges:
    - from: "fraud.timeout-ms"
      to: "fraud.client.timeout-ms"
      defaultChanged:
        from: 500
        to: 2000
      policy: "Preserve old effective behavior unless task explicitly requests new default."

Ini membuat agent tidak perlu menebak.


15. Step 9 — Deterministic repair first

Sama seperti Part 041: jangan gunakan LLM untuk semua hal.

Deterministic rules:

import com.acme.fraud.client.* -> import com.acme.fraud.api.*
FraudResult.isBlocked()       -> FraudResult.decision() == Decision.BLOCK
result.getScore()             -> result.score().value() when context confirms 0..100
config fraud.timeout-ms       -> fraud.client.timeout-ms preserving value

Patch contoh:

-import com.acme.fraud.client.FraudClient;
-import com.acme.fraud.client.FraudResult;
+import com.acme.fraud.api.Decision;
+import com.acme.fraud.api.FraudGateway;
+import com.acme.fraud.api.FraudResult;

 public final class FraudCheckService {
-  private final FraudClient fraudClient;
+  private final FraudGateway fraudGateway;

-  public FraudCheckService(FraudClient fraudClient) {
-    this.fraudClient = fraudClient;
+  public FraudCheckService(FraudGateway fraudGateway) {
+    this.fraudGateway = fraudGateway;
   }

   public FraudDecision check(Transaction transaction) {
-    FraudResult result = fraudClient.check(toRequest(transaction));
-    if (result.isBlocked()) {
+    FraudResult result = fraudGateway.assess(toAssessmentRequest(transaction));
+    if (result.decision() == Decision.BLOCK) {
       return FraudDecision.blocked();
     }
-    if (result.getScore() >= 80) {
+    if (result.score().value() >= 80) {
       return FraudDecision.review();
     }

Jika constructor/injection berubah terlalu banyak, agentic repair boleh masuk.


16. Step 10 — Agentic residual repair

Agentic repair hanya diberi problem terlokalisasi.

Jangan beri instruksi:

Fix the build.

Itu terlalu luas.

Beri instruksi seperti:

Repair compile errors in payment-core/src/main/java/acme/payment/FraudCheckService.java caused by fraud-client 2.x migration.
Allowed files:
- payment-core/src/main/java/acme/payment/FraudCheckService.java
- payment-core/src/test/java/acme/payment/FraudCheckServiceTest.java
- payment-core/src/main/resources/application.yml

Migration rules:
- FraudClient -> FraudGateway
- FraudClient.check(TransactionRequest) -> FraudGateway.assess(FraudAssessmentRequest)
- FraudResult.isBlocked() -> FraudResult.decision() == Decision.BLOCK
- FraudResult.getScore() -> FraudResult.score().value() only if preserving 0..100 scale

Do not change unrelated payment behavior.
Do not relax tests.
Run compile verifier after patch.

Loop:

Bound repair iteration:

agenticRepair:
  maxIterations: 4
  maxFilesPerIteration: 5
  maxAddedLines: 300
  forbiddenActions:
    - skip-tests
    - delete-tests
    - broad-formatting
    - change-public-api-without-approval

17. Step 11 — Test repair tanpa test laundering

Test sering gagal setelah dependency upgrade. Itu normal.

Yang tidak normal adalah agent memperbaiki test dengan cara menghilangkan assertion.

Contoh anti-pattern:

- assertEquals(FraudDecision.BLOCKED, decision);
+ assertNotNull(decision);

Itu test laundering.

Policy:

testPolicy:
  disallowAssertionWeakening: true
  disallowTestDeletion: true
  disallowDisabledTests: true
  requireBehavioralEquivalenceNote: true

Test repair yang valid:

- when(fraudClient.check(any())).thenReturn(FraudResult.blocked(95));
+ when(fraudGateway.assess(any())).thenReturn(
+     FraudResult.of(Decision.BLOCK, RiskScore.of(95))
+ );

  FraudDecision decision = service.check(transaction);
  assertEquals(FraudDecision.BLOCKED, decision);

Verifier harus mencari sinyal buruk:

@Disabled baru
Assumption baru
assertTrue(true)
assertNotNull menggantikan assertion spesifik
penghapusan test file
perubahan threshold tanpa alasan

LLM-as-judge boleh membantu menilai test semantics, tetapi deterministic policy tetap wajib.


18. Step 12 — Runtime behavior guard

Compile/test passing tidak cukup jika dependency mengubah default behavior.

Dalam contoh kita:

fraud-client 1.x default timeout = 500ms
fraud-client 2.x default timeout = 2000ms

Jika target upgrade adalah security fix, mungkin kita ingin preserving behavior.

Patch config:

 fraud:
-  timeout-ms: 500
+  client:
+    timeout-ms: 500

PR evidence:

Runtime behavior note:
- fraud-client 2.x changes default timeout from 500ms to 2000ms.
- This PR preserves the previous effective timeout by migrating config key `fraud.timeout-ms` to `fraud.client.timeout-ms` with value 500.

Agent harus membedakan:

API compatibility repair
Runtime behavior preservation
Intentional behavior adoption
Unknown behavior risk

Jika tidak tahu, tulis unknown risk. Jangan pura-pura aman.


19. Step 13 — Dependency graph judge

Buat judge khusus untuk dependency diff.

Input:

Upgrade request
Before dependency tree
After dependency tree
POM diff
Source diff
Verification report

Checks:

1. Apakah target dependency benar berubah ke versi yang diminta?
2. Apakah hanya satu direct dependency yang diubah?
3. Apakah transitive changes dijelaskan?
4. Apakah ada downgrade tidak disengaja?
5. Apakah ada artifact baru high-risk?
6. Apakah scope berubah?
7. Apakah dependency test-only bocor ke compile/runtime?
8. Apakah public API project sekarang mengekspos dependency baru?

Structured output:

{
  "verdict": "PASS_WITH_NOTES",
  "notes": [
    "Target dependency upgraded correctly",
    "Transitive major upgrade com.acme:fraud-model included",
    "No dependency scope expansion detected",
    "Runtime timeout default preserved via config migration"
  ],
  "requiresHumanAttention": [
    "fraud-spi added as compile dependency; reviewer should confirm expected"
  ]
}

20. Step 14 — Diff boundary judge

Dependency upgrade PR harus kecil dan explainable.

Allowed files:

pom.xml
module pom.xml files
source files with direct API references
test files with direct API references
config files containing migrated config keys
migration evidence artifacts, if committed by policy

Suspicious files:

unrelated service classes
large formatting-only changes
generated code without generator command evidence
CI config unrelated to build failure
README mass rewrite
lock/config file with no explanation

Judge prompt:

Review whether this diff stays within the dependency upgrade boundary.
The task is upgrading com.acme:fraud-client 1.8.7 -> 2.1.0.
Allowed change types:
- Maven declaration/version update
- source repairs caused by fraud-client API changes
- test fixture updates caused by fraud-client API changes
- config key migration for fraud-client timeout
Reject if:
- unrelated business logic changed
- tests were weakened
- broad formatting hides semantic changes
- dependency scope expanded without explanation
Return structured verdict.

21. PR body template

PR yang baik tidak hanya berkata “updated dependency”.

## Summary
Upgrades `com.acme:fraud-client` from `1.8.7` to `2.1.0`.

## Why
Requested dependency upgrade: SECURITY_FIX / PLATFORM_STANDARDIZATION.

## Dependency graph impact
- Direct dependency changed: `com.acme:fraud-client 1.8.7 -> 2.1.0`
- Transitive changes:
  - `com.acme:fraud-model 1.3.0 -> 2.0.1`
  - `com.acme:fraud-spi 2.1.0` added
- No dependency scope expansion detected.

## Source changes
- Migrated `FraudClient` usage to `FraudGateway`.
- Replaced `FraudResult.isBlocked()` with `decision() == Decision.BLOCK`.
- Replaced score access with `score().value()` where scale preservation was verified.
- Updated affected tests without weakening assertions.

## Runtime behavior
- Preserved previous effective timeout of 500ms by migrating config key.

## Verification
- Baseline compile before change: PASS
- Baseline dependency tree captured: PASS
- Compile after upgrade: PASS
- Unit tests affected modules: PASS
- Diff boundary judge: PASS_WITH_NOTES

## Reviewer notes
Please confirm that added transitive dependency `com.acme:fraud-spi` is expected for fraud-client 2.x.

Ini adalah evidence-oriented PR.


22. Implementation sketch: orchestrator

public final class DependencyUpgradeOrchestrator {

  public RunResult run(UpgradeRequest request) {
    PolicyDecision policy = policyGate.evaluate(request);
    if (!policy.canAnalyze()) {
      return RunResult.blocked(policy.reason());
    }

    Workspace workspace = repositoryIngestion.prepare(request.repositoryId(), request.baseRef());

    VerificationReport baseline = verifier.runBaseline(workspace, request.verificationProfile());
    if (!baseline.passed()) {
      return RunResult.analysisOnly("Baseline failed", baseline);
    }

    DependencyGraphSnapshot before = dependencyAnalyzer.snapshot(workspace);
    VersionDeclaration declaration = dependencyAnalyzer.locateDeclaration(workspace, request.from());

    Patch versionPatch = pomEditor.updateVersion(declaration, request.to().version());
    patchService.apply(workspace, versionPatch);

    DependencyGraphSnapshot after = dependencyAnalyzer.snapshot(workspace);
    DependencyGraphDiff graphDiff = dependencyAnalyzer.diff(before, after);

    RiskAssessment risk = riskClassifier.classify(request, graphDiff);
    if (!policy.canPatch(risk)) {
      return RunResult.analysisOnly("Risk requires approval", graphDiff, risk);
    }

    RepairLoopResult repair = repairLoop.repairUntilCompilePasses(workspace, request, graphDiff);
    VerificationReport tests = verifier.runTests(workspace, request.verificationProfile());

    JudgeReport judge = judge.evaluate(workspace, request, graphDiff, repair, tests);

    if (!judge.allowsPr()) {
      return RunResult.failed("Judge rejected diff", judge);
    }

    PullRequestDraft pr = prBuilder.build(request, graphDiff, repair, tests, judge);
    return RunResult.prReady(pr);
  }
}

Perhatikan tidak ada llm.fixEverything().

LLM hanya masuk pada repair loop yang scope-nya sudah dipersempit.


23. Failure model khusus dependency upgrade

FailurePenyebabGuard
Wrong declaration editedVersi berasal dari parent/BOM/propertyEffective model + declaration locator
Build already brokenTidak ada baselineBaseline verifier wajib
Hidden transitive riskHanya lihat direct dependencyGraph snapshot before/after
Test launderingAgent melemahkan assertionTest diff policy + judge
Unrelated refactorAgent memperbaiki build terlalu luasDiff boundary
Runtime regressionDefault config berubahMigration guide + config impact scan
Dependency scope leakTest dep jadi compile depScope diff check
Version downgradeMediation berubahGraph diff detects downgrade
PR unreviewableTerlalu banyak dependency sekaligusMax 1 direct dependency per run
Security theaterCVE fix tapi vulnerable transitive tetap reachableVulnerability scan + reachability note bila tersedia

24. Strong invariant checklist

Sebelum PR dibuat, semua ini harus benar:

[ ] Baseline status diketahui.
[ ] Target dependency declaration ditemukan dengan provenance.
[ ] Patch versi minimal dan benar.
[ ] Dependency graph before/after tersimpan sebagai artifact.
[ ] Transitive changes diringkas.
[ ] Compile verifier pass setelah upgrade/repair.
[ ] Test verifier dijalankan sesuai profile.
[ ] Tidak ada test weakening yang terdeteksi.
[ ] Runtime config impact dinyatakan.
[ ] Diff boundary judge pass.
[ ] PR body menyebut unresolved risk.

Jika salah satu tidak terpenuhi, jangan buat PR sebagai “ready”. Turunkan menjadi draft atau analysis-only.


25. Apa yang harus dicatat ke database

Tambahkan artifact records:

create table dependency_upgrade_reports (
  id uuid primary key,
  run_id uuid not null,
  target_group_id text not null,
  target_artifact_id text not null,
  from_version text not null,
  to_version text not null,
  declaration_kind text not null,
  declaration_file text not null,
  before_tree_artifact_id uuid not null,
  after_tree_artifact_id uuid not null,
  graph_diff_artifact_id uuid not null,
  risk_level text not null,
  created_at timestamptz not null default now()
);

Dan audit event:

{
  "eventType": "DependencyUpgradeGraphDiffCreated",
  "runId": "...",
  "target": "com.acme:fraud-client",
  "from": "1.8.7",
  "to": "2.1.0",
  "riskLevel": "HIGH",
  "directChanges": 1,
  "transitiveChanges": 12,
  "artifactUri": "artifact://runs/.../dependency-graph-diff.json"
}

Dependency upgrade adalah perubahan supply-chain. Audit-nya harus first-class.


26. Latihan implementasi

Untuk menguasai part ini, jangan langsung pakai repo besar.

Buat repo kecil dengan 3 module:

payment-parent
  payment-api
  payment-core
  payment-service

Buat library lokal versi 1 dan versi 2:

fraud-client-v1
fraud-client-v2

Publikasikan ke local Maven repository:

mvn install

Lalu buat agent task:

task:
  type: DEPENDENCY_UPGRADE
  dependency: com.acme:fraud-client
  from: 1.8.7
  to: 2.1.0
  mode: MAJOR_WITH_REPAIR

Target kelulusan:

1. Agent mengubah versi di lokasi yang benar.
2. Agent menghasilkan dependency graph diff.
3. Agent memperbaiki compile error.
4. Agent tidak mengubah file unrelated.
5. Agent memperbarui test tanpa melemahkan assertion.
6. Agent menghasilkan PR body evidence-oriented.

27. Ringkasan

Dependency upgrade otomatis adalah salah satu use case terbaik untuk Honk-like AI coding agent karena:

  • repetitive,
  • bernilai tinggi,
  • punya verifier jelas,
  • bisa dibatasi scope-nya,
  • cocok untuk PR workflow.

Tetapi ia juga salah satu use case yang paling mudah diremehkan.

Mental model yang harus dipegang:

Dependency upgrade = graph transition + source repair + runtime risk management.

Agent yang matang tidak hanya mengubah versi. Ia membuktikan:

Apa yang berubah?
Kenapa berubah?
Apa dampaknya?
Apa yang diverifikasi?
Apa risiko yang tersisa?

Di part berikutnya kita akan memperluas dari dependency/code API migration ke config dan schema migration: YAML, JSON, OpenAPI, dan database schema. Ini lebih sulit karena compatibility-nya tidak hanya compile-time, tetapi juga melibatkan producer-consumer contract, rollout bertahap, dan backward compatibility.


Referensi

Lesson Recap

You just completed lesson 43 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.