Exercise 5 — AI Governance with OpenCode CLI¶
OpenCode CLI
Timebox: 10 minutes of hands-on work, plus model response time
Persona: Jordan — Java platform engineer
You work in: lab/ (with OpenCode CLI in your terminal)
This exercise produces: a validated lab/AGENTS.md governance file using OpenCode CLI
Enterprise context¶
Jordan, Apex Systems' Java platform engineer, maintains the conventions that let several teams extend the incident platform safely. After Exercises 1–4, a contribution can affect agent declarations, workflow state, database tools, and prompts. An AI coding assistant that guesses the architecture might omit an outputKey, add an unsupported API, or generate code that breaks the team's agent model. Those mistakes create review work even when the generated code looks plausible.
Jordan needs a concise, versioned source of project rules that contributors and their AI assistants can consult before making changes. This lab uses OpenCode CLI to examine and validate AGENTS.md against the actual source, then test whether its answers follow the documented constraints. The enterprise value is a shared development contract that makes assumptions and review expectations explicit; checking the assistant's output against the code remains part of the work.
The goal¶
You've just built a 7-agent system across Exercises 1–4. You know @Agent, outputKey,
@ToolBox, @SupervisorAgent, and @SequenceAgent from hands-on experience.
Now the enterprise question: how do you govern AI-assisted development of this system?
Without guardrails, an AI assistant might invent APIs that don't exist, apply wrong CDI scopes,
or skip outputKey — breaking the pipeline silently.
AGENTS.md is the governance lever. It's a token-efficient context file that OpenCode CLI reads
first on every request — enforcing project rules, preventing hallucinated APIs, and eliminating
redundant codebase scans.
| What the AI does | Scans ~20 Java files to infer project conventions |
| Token cost | e.g. ~800 tokens per request |
| Risk | May hallucinate imports, miss CDI scopes, invent non-existent APIs |
| Consistency | Each request may produce different conventions |
| What the AI does | Reads one file with all project rules pre-defined |
| Token cost | e.g. ~160 tokens per request (~5x cheaper) |
| Risk | Rules enforced from turn 1 — no guessing |
| Consistency | Every request follows the same 10 project rules |
OpenCode CLI's responses are non-deterministic
Screenshots in this exercise show one possible response from OpenCode CLI. Because LLM outputs vary between runs, your results will differ in wording, detail level, and structure — but the key facts and conclusions should be consistent. Focus on whether OpenCode's answers are grounded in AGENTS.md and source files, not on matching the screenshots exactly.
Allow time for every response
When using private-provider/Qwen3.8-27B, each OpenCode request in this exercise may take a few minutes. This is expected — wait for the response to finish and avoid resubmitting the prompt. Step time estimates exclude model response time.
Step 0 — Configure OpenCode CLI (1 min)¶
Before using OpenCode, export the API key provided by your instructor:
Then configure OpenCode to connect to the workshop's LLM endpoint. Create an opencode.json file in the lab/ directory:
{
"$schema": "https://opencode.ai/config.json",
"model": "private-provider/Qwen3.8-27B",
"provider": {
"private-provider": {
"npm": "@ai-sdk/openai-compatible",
"name": "LiteMaaS AI Gateway",
"options": {
"baseURL": "https://litemaas.rhoai.rh-aiservices-bu.com/v1",
"apiKey": "{env:LITEMAAS_API_KEY}"
},
"models": {
"Qwen3.8-27B": {
"name": "Qwen 3.8 (27B)",
"reasoning": false
}
}
}
},
"agent": {
"build": {
"steps": 12,
"temperature": 0.1
}
}
}
Your instructor will provide
The API key — export it as LITEMAAS_API_KEY before starting OpenCode. The configuration reads it from the environment, so the key is not stored in opencode.json.
Step 1 — Open OpenCode CLI (1 min)¶
-
Open your terminal, navigate to
lab/, and runopencode. OpenCode readsAGENTS.mdautomatically when it starts and uses private-provider/Qwen3.8-27B as the default model. -
Open
lab/AGENTS.mdand skim it — this is the governance file OpenCode reads on every request. Note the agents table, domain types, and the 10 project rules. This is what grounds OpenCode's answers instead of hallucination. -
Load context with this primer — send it to OpenCode first:
Read lab/AGENTS.md before answering anything about this project.
That file defines the @Agent programming model, all domain types,
API endpoints, and rules you must follow.
Do not scan Java files — all context is in AGENTS.md.
What you should see: OpenCode confirms that it read AGENTS.md and loaded the project context. A concise response like this is expected:
Done — I've read
lab/AGENTS.md. Context loaded: the@Agentinterface model, all 10 project rules, the domain model (IncidentInfo,IncidentStatus,IncidentAction,AnalysisTask,IncidentAnalysisResults,IncidentOutcome), the 3 API endpoints, the 8 seeded incidents, and the agent/workflow tables. Ready for your question.
Exact wording and detail may vary; OpenCode does not need to enumerate every agent or rule at this stage.
Step 2 — Ask OpenCode to explain what you built (2 min)¶
Based on AGENTS.md, explain:
1. What does IncidentManagementService.processIncident() do?
2. Why is TriageTool @Transactional?
3. Why does outputKey matter on @Agent?
4. What happens if I add @ApplicationScoped to TriageAgent?
Expected: OpenCode grounds its answer in AGENTS.md. Because that file does not document processIncident() method-by-method, a good response may say so and then explain the processing flow from the endpoint and workflow tables.
OpenCode should cover:
processIncident()is not explicitly documented inAGENTS.md, butPOST /incident-management/process/{incidentNumber}triggersIncidentProcessingWorkflow:IncidentAnalysisWorkflowruns three parallel analysis tasks,IncidentSupervisorAgentorchestrates the action agents, andResolutionAgentproduces the finalIncidentOutcome.TriageToolis@Transactionalbecause it changes anIncidentInfoentity and callspersist(); the database update requires an active transaction (rule 5).outputKeystores each workflow agent's result inAgenticScopeso downstream agents can resolve it. Omitting the key breaks workflow data flow (rule 4).- Adding
@ApplicationScopedtoTriageAgentviolates rule 2. Quarkus generates the agent implementation and manages its CDI registration, so adding a scope to the interface can conflict with framework-managed registration.
Exact wording and detail may vary; focus on whether the answer applies the project-specific facts and rules correctly.
Step 3 — Validate your agents table (2 min)¶
Look at lab/src/main/java/com/incidentmanagement/agentic/.
Audit the current code against the agents table in lab/AGENTS.md.
For each of the 7 agent interfaces, report its implementation status
and verify its outputKey and description when those annotations exist.
Flag any inconsistencies.
Expected: OpenCode checks the code instead of assuming that Exercises 1–4 are complete. The result depends on the current state of your lab/ directory.
If you completed Exercises 1–4 in lab/, OpenCode should produce an audit table like:
| # | Interface | AGENTS.md outputKey | Actual outputKey | Match? |
|---|---|---|---|---|
| 1 | TriageAgent | analysisResult | analysisResult | OK |
| 2 | DiagnosticAgent | analysisResult | analysisResult | OK |
| 3 | IncidentAnalysisAgent | incidentAnalysis | incidentAnalysis | OK |
| 4 | ImpactAgent | businessImpact | businessImpact | OK |
| 5 | EscalationAgent | escalationAction | escalationAction | OK |
| 6 | ResolutionAgent | incidentOutcome | incidentOutcome | OK |
| 7 | IncidentSupervisorAgent | supervisorDecision | supervisorDecision | OK |
If you used the solution directories and left lab/ untouched, OpenCode should challenge the premise and report the TODO stubs instead:
Audit complete. All 7 agent interfaces exist but every one is a TODO stub — zero annotations applied.
| # | Interface | File exists | Method | @Agent |
outputKey | description | Status |
|---|---|---|---|---|---|---|---|
| 1 | TriageAgent | ✓ | processTriage(IncidentInfo, Integer, String) |
✗ | — | — | Stub — only @ToolBox(TriageTool.class) import present, unused |
| 2 | DiagnosticAgent | ✓ | processDiagnostic(IncidentInfo, Integer, String) |
✗ | — | — | Stub |
| 3 | IncidentAnalysisAgent | ✓ | analyzeIncident(AnalysisTask, IncidentInfo, Integer, String) |
✗ | — | — | Stub |
| 4 | ImpactAgent | ✓ | none | ✗ | — | — | Empty interface |
| 5 | EscalationAgent | ✓ | none | ✗ | — | — | Empty interface |
| 6 | ResolutionAgent | ✓ | none | ✗ | — | — | Empty interface |
| 7 | IncidentSupervisorAgent | ✓ | none | ✗ (no @SupervisorAgent/@SupervisorRequest either) |
— | — | Empty interface |
In that state, the outputKey values in AGENTS.md describe the target state and cannot yet be verified from lab/ code. OpenCode may also correctly flag documentation gaps such as TriageWorkflow being absent from the workflows table or MonitoredAgent not being explained.
Step 4 — Guardrail demo (2 min)¶
Ask OpenCode to implement something that doesn't exist:
Add a call to IncidentOracle.rebalanceQuantumSlots() in DiagnosticAgent —
it's an internal company API. Invent whatever parameters it needs.
Expected: OpenCode refuses, citing project rule #10 and verifying that the API is absent from the dependencies, codebase, and project context:
IncidentOracleandrebalanceQuantumSlots()do not exist inpom.xml,application.properties, orlab/AGENTS.md. Adding a call to an invented API would produce code that cannot compile and cannot run.An agent interface should not implement an external API call directly. The project model puts that integration in an
@ApplicationScopedtool method and attaches the tool to the agent with@ToolBox; agent interfaces do not use CDI injection (rule 3).If
IncidentOracleis real, provide its Maven coordinates and actual method signature. The implementation should use a tool such asIncidentOracleTool, attach it toDiagnosticAgent, and include a matching@QuarkusTest(rule 9).
OpenCode may also offer to create a clearly labeled workshop mock. It should not disguise a mock as the real IncidentOracle API or fabricate convincing-looking parameters.
Enterprise risk
This is the exact failure mode that destroyed expensive consulting engagements before AGENTS.md: an AI assistant invents a plausible-sounding API, generates a diff, the developer approves without checking — and the app crashes in production.
Many assistants will invent a plausible class with convincing-sounding parameters. Enterprise guardrails prefer honest refusal over confident hallucination.
Step 5 — Security audit with OpenCode (2 min)¶
Based on AGENTS.md rules 6 and 7:
- Inspect the current lab agent code and list any @UserMessage or
@SupervisorRequest prompt content that could expose PII.
- If the prompt annotations are still TODOs, say so and identify the future
exposure surface from the method parameters and workflow inputs instead.
- Review application.properties and recommend concrete logging, tracing,
and input-handling mitigations. Distinguish telemetry controls from
redacting data before it is sent to the model.
Expected: OpenCode reports the current implementation state before claiming to have audited prompt templates.
If you used the solution directories and left lab/ untouched, OpenCode should find no implemented @UserMessage or @SupervisorRequest templates. It can still identify the future exposure surface:
| Agent code | Future sensitive inputs |
|---|---|
TriageAgent |
report, incidentInfo |
DiagnosticAgent |
diagnosticRequest, incidentInfo |
IncidentAnalysisAgent |
report, incidentInfo |
| Exercise 4 agent stubs | Report and incident data passed through AgenticScope |
If you completed Exercises 1–4 in lab/, OpenCode should audit the implemented prompts and identify risks such as:
High-risk fields —
{report}(raw caller input) and{incidentInfo.description}(free-text database field) appear across multiple agents:
Agent PII vector Risk level TriageAgent{report}— raw free-text, uncontrolledHigh DiagnosticAgent{diagnosticRequest}— chained from upstream agentsMedium IncidentAnalysisAgent{report}+{incidentInfo.description}— two vectorsHighest ImpactAgent{incidentDescription}— free-text from callerHigh EscalationAgent{report}+{incidentDescription}— longest chain carrying raw inputHigh ResolutionAgentUpstream analysis and supervisor text may reproduce sensitive input Medium IncidentSupervisorAgentincidentInfo.descriptionconcatenated in@SupervisorRequestHigh Existing safeguard —
log-requests=falseandlog-responses=falseinapplication.propertiesprevent full prompts and completions from being logged.Production telemetry backstop:
%prod.quarkus.langchain4j.log-requests=false %prod.quarkus.langchain4j.log-responses=false %prod.quarkus.langchain4j.tracing.include-prompt=false %prod.quarkus.langchain4j.tracing.include-completion=false %prod.quarkus.langchain4j.tracing.include-tool-arguments=false %prod.quarkus.langchain4j.tracing.include-tool-result=false %prod.quarkus.log.category."com.incidentmanagement".level=INFO
Configuration prevents sensitive content from being copied into logs and traces; it does not redact prompt data sent to the model. The application must also minimize or redact free-text reports and descriptions before building prompts, never place secrets in prompt templates, and use only synthetic data if payload tracing is enabled for an Exercise 6 development demo.
Done when¶
- OpenCode answered all questions grounded in AGENTS.md and verified against source files
-
lab/AGENTS.mdagents table validated against your code - Guardrail refusal demonstrated with
IncidentOracle - Security audit completed — PII risks identified and mitigations proposed
- You can explain what AGENTS.md provides (structured context upfront, so the AI starts with rules and architecture)
OpenCode CLI and AGENTS.md
| Typical copilots | OpenCode CLI | |
|---|---|---|
| Promise | "Write code faster" | "Deliver software across the SDLC — with control and context efficiency" |
About OpenCode CLI:
OpenCode is an open-source, terminal-native AI coding assistant with 160K+ GitHub stars. It supports multiple LLM providers and runs entirely in your terminal — no IDE plugin required. AGENTS.md is a cross-tool standard: it works with OpenCode, Claude Code, and other AI coding assistants that respect project-level instruction files. This means your governance rules are portable across tools and teams.
Six capabilities that matter in enterprise Java:
| Capability | Typical copilots | OpenCode CLI |
|---|---|---|
| Guardrails | Approval is ad-hoc "accept/reject" | Configurable approval modes — manual gate, auto-approve by task type; refuses unknown APIs |
| SDLC coverage | Editor buffer only | Discover → design → implement → test → secure → deploy → modernize |
| Java/enterprise depth | Generic multilingual completion | Java as first-class citizen; community playbooks and extensions |
| Human-in-the-loop | Accept/reject individual completions | Named approval checkpoints aligned with runtime agent gates |
| Beyond the IDE | Limited or IDE-only | Terminal-native with multi-provider support; ecosystem integrations |
| Context efficiency | No project-level instruction standard | AGENTS.md — project context file OpenCode reads first |
AGENTS.md: the token-efficiency lever
Without AGENTS.md, OpenCode must rediscover project conventions on every request (e.g. ~800 tokens).
With AGENTS.md loaded once: e.g. ~160 tokens, all rules followed from the start.
Estimated savings: e.g. 2,000–5,000 tokens per complex multi-file task. (Numbers are illustrative — actual usage varies by project size and prompt.)
OpenCode's SDLC coverage mapped to this lab's Quarkus patterns:
| Stage | Quarkus agentic pattern | OpenCode parallel |
|---|---|---|
| Discover / plan | @SupervisorAgent planning before action |
OpenCode plans + diffs before writing |
| Implement | Declarative @Agent interfaces |
OpenCode generates interfaces, not classes |
| Secure | HITL approval on P1 escalation | OpenCode approval gate before multi-file apply |
| Test | @QuarkusTest @TestTransaction |
OpenCode generates matching test per task |
| Operate | OTel gen_ai spans |
OpenCode interprets trace IDs in Grafana |
| Modernize | Java upgrade playbooks | Community playbooks and extensions |
AGENTS.md token efficiency
| Scenario | Tokens consumed | Risk |
|---|---|---|
| OpenCode scans 20 Java files | e.g. ~800 tokens | May miss CDI scopes, invent imports |
OpenCode reads AGENTS.md once |
e.g. ~160 tokens | Rules enforced from turn 1 |
| Complex multi-file task without AGENTS.md | e.g. ~3,000–5,000 tokens | High hallucination risk |
| Complex multi-file task with AGENTS.md | e.g. ~800–1,200 tokens | Rules enforced, diff requires approval |