docs: design the portable solution architect agent
Some checks failed
validation / verify (push) Failing after 10s

Record the direction and build plan for replacing the upstream
google-cloud-solution-architecture skill with a portable agent.

- ADR-0002: Agent Skills are the portable unit of behaviour, loaded by
  the framework's native skill runtime; discovery and grounding use
  each Cloud provider's hosted remote MCP servers.
- ADR-0003: LangGraph holds Execution state, checkpoints and
  interrupts; a ReAct Orchestrator and Specialists run as ADK
  LlmAgents with SkillToolset. Supersedes ADR-0001.
- CONTEXT.md: domain glossary (Execution, Phase, Approval, Revision,
  Orchestrator, Specialist, Deliverable, Dependency, Cloud provider).
- .scratch/solution-architect-agent/spec.md: build spec with the
  dependency graph, Revision rules, tool tiers, MCP allowlists, A2A
  interaction, tests and nine build increments.
- .scratch/phase-pipeline/spec.md: superseded; kept as decision log.
- CLAUDE.md and docs/agents/: agent skill configuration (local
  markdown issue tracker, triage labels, domain docs).
- README.md: direction note; flags current ADK classes as stubs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 18:09:26 +01:00
parent a24a44e28c
commit 4fefe0ab33
11 changed files with 1081 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
---
status: superseded by ADR-0003
---
# LangGraph orchestrates Phases, not Google ADK
Phases are orchestrated by a LangGraph `StateGraph`, as the KAB Workflow Agent standard (`kyndryl-agent-builder/docs/KAB_WORKFLOW_AGENT_IMPLEMENTATION_GUIDE.md`) requires, rather than by Google ADK `Runner`/`LoopAgent`. The earlier "ADK" orchestration was a local compatibility layer that never used the real `google-adk` package, so it added a pretend seam with one adapter and diverged from the platform standard; we deleted it. Real ADK remains welcome *inside* a Phase (e.g. an `LlmAgent` for LLM-driven design) — just not as the thing that sequences Phases.
> **Reopened (2026-09-15).** [ADR-0002](0002-skills-are-the-portable-unit-of-behaviour.md) makes Skills, loaded by a framework's own skill runtime, the portable unit of behaviour. This ADR assumed our code sequences the Phases. If the Skill sequences them instead, the choice here reduces to which skill runtime to wire first, and "ADK only inside a Phase" no longer holds. Still true regardless: the compat stubs are fiction and should go.
## Considered Options
- **Real `google-adk` as orchestrator**: rejected; departs from the KAB workflow standard and would require async event-stream plumbing for no capability LangGraph lacks (conditional edges cover the Review round).
- **Plain in-process pipeline, no framework**: rejected; neither KAB template uses it, and LangGraph already gives us conditional routing and a path to checkpointing if a human approval gate is added later.

View File

@@ -0,0 +1,25 @@
---
status: accepted
---
# Skills are the portable unit of behaviour
This agent exists to replace the [`google-cloud-solution-architecture`](https://github.com/google/skills/blob/main/skills/cloud/google-cloud-solution-architecture/SKILL.md) skill with an agent that runs anywhere. Its solution-architecture behaviour lives in Skills that follow the [Agent Skills specification](https://agentskills.io/specification) (`SKILL.md` plus `references/`, `assets/` and `scripts/`). The agent framework's own skill runtime loads them: Google ADK's `SkillToolset`, or LangChain's skills pattern (Deep Agents `skills=` / `SkillsMiddleware`). Our code supplies the tooling those Skills call on: grounding, IaC validation and environment scanning. It doesn't restate the work the Skills describe.
Google Cloud is the first and, for now, only Cloud provider. Other hyperscalers should be addable as new Skills plus new tooling, without rewriting the agent's core.
## Refinements (2026-09-15)
- **Hybrid sequencing.** Our code owns Phase boundaries and the Approvals between Phases. Within a Phase, the Skill, run by the skill runtime, decides the work. We rejected letting the Skill sequence everything: Phase checkpoints give us resumable, recordable, testable progress. We rejected code-owned Phase bodies because they would bypass the Skill.
- **Hierarchical and ReAct, not linear.** Work isn't a fixed chain. Agents are organised in a hierarchy that follows the dependencies between Deliverables. Work whose dependencies are satisfied may proceed independently, and every agent reasons and acts in a ReAct loop of thought, tool call and observation. An Orchestrator sits above one Specialist per Deliverable, and a Specialist fans out further only where its work splits into independent parts. Code declares the dependency graph, and the Orchestrator chooses what to do next within it. The engine is recorded in [ADR-0003](0003-langgraph-holds-state-adk-agents-reason.md).
- **Interactive by default.** Executions are multi-turn with user Approval, as the upstream Skill requires. A pre-approved mode serves batch and evaluation runs.
- **Vendored upstream Skill.** `google-cloud-solution-architecture` is copied unmodified and pinned to an upstream commit. Our additions live in separate Skills for our own tooling, so upstream changes arrive as a diff.
- **Discovery through the provider's own MCP servers.** Current-state discovery and documentation grounding go through the remote MCP servers each Cloud provider hosts, for example Google Cloud's Resource Manager, Asset Inventory, Compute, GKE and Developer Knowledge servers, and the AWS MCP Server. Code carries no hand-written cloud API clients. Only reviewed read-only MCP tools are exposed, and reading a user's cloud requires their permission.
- **Cloud provider as configuration.** A Cloud provider is a configured bundle of Skills, tools and remote MCP servers. There's no provider interface in code until a second Cloud provider exists, but core module names carry no provider prefix.
## Consequences
- Framework-specific code is confined to wiring a skill runtime and registering tools, so moving to another framework is a new wiring, not a rewrite.
- Skills in `app/skills/` must conform to the specification: kebab-case `name` matching the directory, a `description` of when to use the Skill, and no custom `phase:` key. They don't today.
- This reopens [ADR-0001](0001-langgraph-orchestrates-phases-not-google-adk.md) and the Phase pipeline spec (`.scratch/phase-pipeline/spec.md`). [ADR-0003](0003-langgraph-holds-state-adk-agents-reason.md) settles the engine and supersedes ADR-0001.
- Both known skill runtimes are young: ADK marks Skills *Experimental* (Python ≥ 1.25.0), and Deep Agents skills are new. Pin versions.

View File

@@ -0,0 +1,25 @@
---
status: accepted
supersedes: ADR-0001
---
# LangGraph holds Execution state; ADK agents reason and act
An Execution is a hierarchy of ReAct agents shaped by the dependencies between Deliverables ([ADR-0002](0002-skills-are-the-portable-unit-of-behaviour.md)).
- **LangGraph holds the Execution.** A LangGraph `StateGraph` holds the dependency graph and each Deliverable's state. It checkpoints after every step and pauses with `interrupt()` whenever the user must answer or approve. This follows the KAB workflow standard and the platform's `langgraph-workflow-agent`.
- **ADK agents reason and act.** The Orchestrator and every Specialist is an ADK `LlmAgent`, each running its own ReAct loop, with a `SkillToolset` for progressive Skill loading. This follows the platform's `google-adk-base-agent/app/skills_integration.py`.
- **Agents sit behind one interface.** Each agent runs behind a single agent-runtime interface, so LangChain Deep Agents can be added as a second implementation without touching the graph.
The dependency graph is declared as data in code. The Orchestrator picks what to do next through tools that refuse work whose dependencies aren't met. Dependencies are guaranteed by code, while ordering and parallelism are the model's choice.
## Considered Options
- **ADK end to end** (root `LlmAgent` with `sub_agents`/`AgentTool`, and ADK sessions for pauses): rejected. It departs from the KAB workflow standard, and LangGraph's checkpointed `interrupt()` is the proven platform path for resumable user Approvals.
- **Deep Agents end to end** (`create_deep_agent(skills=, subagents=)`): viable, and it compiles to LangGraph. It was deferred rather than rejected: ADK `SkillToolset` already has working prior art on this platform, and Deep Agents stays reachable as the second implementation of the agent-runtime interface.
- **Code runs the dependency graph with no Orchestrator model**: rejected. The top level wouldn't be ReAct, and the Skill's conversational flexibility (such as skipping work the user says is already approved) would need hand-coding.
## Consequences
- There are two frameworks in one process. Framework types stay inside the agent-runtime implementation and never appear in graph state.
- ADK Skills are *Experimental* (Python ≥ 1.25.0), so pin the version.

51
docs/agents/domain.md Normal file
View File

@@ -0,0 +1,51 @@
# Domain Docs
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
## Before exploring, read these
- **`CONTEXT.md`** at the repo root, or
- **`CONTEXT-MAP.md`** at the repo root if it exists: it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
- **`docs/adr/`**: read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
## File structure
Single-context repo (most repos):
```
/
├── CONTEXT.md
├── docs/adr/
│ ├── 0001-event-sourced-orders.md
│ └── 0002-postgres-for-write-model.md
└── src/
```
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
```
/
├── CONTEXT-MAP.md
├── docs/adr/ ← system-wide decisions
└── src/
├── ordering/
│ ├── CONTEXT.md
│ └── docs/adr/ ← context-specific decisions
└── billing/
├── CONTEXT.md
└── docs/adr/
```
## Use the glossary's vocabulary
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
If the concept you need isn't in the glossary yet, that's a signal: either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
## Flag ADR conflicts
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
> _Contradicts ADR-0007 (event-sourced orders), but worth reopening because…_

View File

@@ -0,0 +1,30 @@
# Issue tracker: Local Markdown
Issues and specs for this repo live as markdown files in `.scratch/`.
## Conventions
- One feature per directory: `.scratch/<feature-slug>/`
- The spec is `.scratch/<feature-slug>/spec.md`
- Implementation issues are one file per ticket at `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from `01`, never a single combined tickets file
- Triage state is recorded as a `Status:` line near the top of each issue file (see `triage-labels.md` for the role strings)
- Comments and conversation history append to the bottom of the file under a `## Comments` heading
## When a skill says "publish to the issue tracker"
Create a new file under `.scratch/<feature-slug>/` (creating the directory if needed).
## When a skill says "fetch the relevant ticket"
Read the file at the referenced path. The user will normally pass the path or the issue number directly.
## Wayfinding operations
Used by `/wayfinder`. The **map** is a file with one **child** file per ticket.
- **Map**: `.scratch/<effort>/map.md` (the Notes / Decisions-so-far / Fog body).
- **Child ticket**: `.scratch/<effort>/issues/NN-<slug>.md`, numbered from `01`, with the question in the body. A `Type:` line records the ticket type (`research`/`prototype`/`grilling`/`task`); a `Status:` line records `claimed`/`resolved`.
- **Blocking**: a `Blocked by: NN, NN` line near the top. A ticket is unblocked when every file it lists is `resolved`.
- **Frontier**: scan `.scratch/<effort>/issues/` for files that are open, unblocked, and unclaimed; first by number wins.
- **Claim**: set `Status: claimed` and save before any work.
- **Resolve**: append the answer under an `## Answer` heading, set `Status: resolved`, then append a context pointer (gist + link) to the map's Decisions-so-far in `map.md`.

View File

@@ -0,0 +1,15 @@
# Triage Labels
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
| Label in mattpocock/skills | Label in our tracker | Meaning |
| -------------------------- | -------------------- | ---------------------------------------- |
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
| `needs-info` | `needs-info` | Waiting on reporter for more information |
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
| `ready-for-human` | `ready-for-human` | Requires human implementation |
| `wontfix` | `wontfix` | Will not be actioned |
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
Edit the right-hand column to match whatever vocabulary you actually use.