docs: add full RFC specs for agent dispatch

AX principles + go/agent + core/agent + php/agent specs.
Temporary — needed in-repo until core-agent mount bug is fixed.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-30 19:51:55 +01:00
parent 540309f5e0
commit be78c27561
32 changed files with 11489 additions and 0 deletions

32
docs/RFC-AGENT-INDEX.md Normal file
View file

@ -0,0 +1,32 @@
# core/agent/
Agent dispatch, pipeline, runner service, plugins, topology.
## Specs
| File | Purpose |
|------|---------|
| [RFC.md](RFC.md) | Agent system (dispatch, daemon, tray, team model) |
| [RFC.pipeline.md](RFC.pipeline.md) | **Pipeline commands** — audit→epic→execute, MetaReader, knowledge accumulation |
| [RFC.topology.md](RFC.topology.md) | Agent topology (Cladius, Charon, local/remote) |
| [RFC.agents-brand.md](../../lthn/RFC.agents-brand.md) | Agent brand identities (in lthn/) |
| [RFC.plugin-restructure.md](RFC.plugin-restructure.md) | Plugin restructure plan |
## Subdirectories
### [flow/](flow/)
Flow system — YAML-defined agent workflows, path-addressed, composable.
### [plugins/](plugins/)
Plugin architecture — Claude, Codex, Gemini, PHP (63 commands/skills).
## Cross-References
| Spec | Relationship |
|------|-------------|
| `code/core/go/agent/RFC.md` | Go implementation (dispatch, workspace, MCP) |
| `code/core/php/agent/RFC.md` | PHP implementation (OpenBrain, content pipeline, sessions) |
| `code/core/mcp/RFC.md` | MCP transport layer agent uses |
| `code/core/config/RFC.md` | `.core/agent.yaml` config spec |
| `project/lthn/ai/RFC.md` | lthn.sh platform (fleet dispatch target) |
| `project/lthn/lem/RFC.md` | LEM training pipeline (agent findings → training data) |

246
docs/RFC-AGENT-PIPELINE.md Normal file
View file

@ -0,0 +1,246 @@
# Agentic Pipeline v2 — Autonomous Dispatch→Verify→Merge
> The full autonomous pipeline: issue → dispatch → implement → verify → PR → merge.
> CodeRabbit findings = 0 is the KPI.
---
## Pipeline Flow
```
Issue created (Forge/GitHub)
→ core-agent picks up event
→ Selects flow YAML based on event type + repo
→ Prepares sandboxed workspace (CODEX.md, .core/reference/)
→ Dispatches agent (codex/gemini/claude)
→ Agent implements in workspace
→ QA flow runs (build, test, vet, lint)
→ If QA passes → create PR to dev
→ CodeRabbit reviews PR
→ If findings = 0 → auto-merge
→ If findings > 0 → dispatch fix agent → repeat
→ PR merged → training data captured
→ Issue closed
```
## Key Design Decisions
### Sandboxing
Agents MUST be sandboxed to their assigned repo. Unsandboxed writes caused the CLI mess
(agent wrote files to wrong repo). Workspace isolation is non-negotiable.
### CodeRabbit KPI
CodeRabbit findings = 0 is the target. Every finding means:
- Template didn't prevent it → fix the template
- Model didn't catch it → add to training data
- Convention wasn't documented → add to RFC
Zero findings = complete convention coverage.
### Checkin API
Agents check in with status via api.lthn.sh. Current blocker: Forge webhooks
need to fire to lthn.sh so the orchestrator knows when to start the pipeline.
### Security Model (from Charon flows)
Orchestrator uses STRUCTURAL signals only (labels, PR state, review counts).
Never parses comment CONTENT — immune to prompt injection via issue comments.
## Agent Pool Configuration
See `code/core/go/agent/RFC.md` §Dispatch & Pool Routing for the full `agent.yaml` schema (concurrency, rates, model variants, agent identities).
Concurrency enforced by runner service (core/agent). Slot reservation prevents
TOCTOU race between parallel dispatches.
## go-process Improvements Needed
- `Timeout` — kill after N minutes (currently agents can run forever)
- `GracePeriod` — SIGTERM before SIGKILL
- `KillGroup` — kill process group, not just PID (prevents orphaned subprocesses)
## Metrics
- 25 repos auto-merged in recent sweep
- 74 findings on core/agent alone (70+ fixed)
- Zero-finding rate improving as templates capture conventions
## `core pipeline` Command Tree (Go Implementation)
```
core pipeline
├── audit <repo> # Stage 1: audit issues → implementation issues
├── epic
│ ├── create <repo> # Stage 2: group issues into epics
│ ├── run <epic-number> # Stage 3: dispatch + monitor an epic
│ ├── status [epic-number] # Show epic progress
│ └── sync <epic-number> # Tick parent checklist from closed children
├── monitor [repo] # Watch all open PRs, auto-intervene
├── fix
│ ├── reviews <pr-number> # "Can you fix the code reviews?"
│ ├── conflicts <pr-number> # "Can you fix the merge conflict?"
│ ├── format <pr-number> # gofmt, commit, push (no AI)
│ └── threads <pr-number> # Resolve all threads after fix
├── onboard <repo> # Full: audit → epic → dispatch
├── budget # Daily usage vs pool
│ ├── plan # Optimal dispatch for today
│ └── log # Append dispatch event
└── training
├── capture <pr-number> # Journal entry for merged PR
├── stats # Summary across journals
└── export # Clean export for LEM training
```
## MetaReader — Structural Signals Only
The core abstraction. Every pipeline decision comes through this interface. **NEVER reads comment bodies, commit messages, PR descriptions, or review content.**
```go
type MetaReader interface {
GetPRMeta(repo string, pr int) (*PRMeta, error)
GetEpicMeta(repo string, issue int) (*EpicMeta, error)
GetIssueState(repo string, issue int) (string, error)
GetCommentReactions(repo string, commentID int64) ([]ReactionMeta, error)
}
```
### PRMeta
```go
type PRMeta struct {
Number int
State string // OPEN, MERGED, CLOSED
Mergeable string // MERGEABLE, CONFLICTING, UNKNOWN
HeadSHA string
HeadDate time.Time
AutoMerge bool
BaseBranch string
HeadBranch string
Checks []CheckMeta
ThreadsTotal int
ThreadsResolved int
HasEyesReaction bool // 👀 = agent acknowledged
}
type CheckMeta struct {
Name string // "qa", "build", "org-gate"
Conclusion string // "SUCCESS", "FAILURE", ""
Status string // "COMPLETED", "QUEUED", "IN_PROGRESS"
}
```
### EpicMeta
```go
type EpicMeta struct {
Number int
State string
Children []ChildMeta
}
type ChildMeta struct {
Number int
Checked bool // [x] vs [ ]
State string // OPEN, CLOSED
PRs []int
}
```
### Security: What's Explicitly Excluded
The MetaReader has NO methods for:
- `GetCommentBodies` — injection vector
- `GetCommitMessages` — can contain crafted instructions
- `GetPRDescription` — attacker-controlled in fork PRs
- `GetReviewThreadContent` — untrusted input
Implementation uses `gh api` with `--jq` filters that strip content at the query level. Content never enters the Go process.
## Three-Stage Pipeline
```
STAGE 1: AUDIT (flow: audit-issues)
Input: Repo with [Audit] issues
Output: Implementation issues (1 per finding)
→ Classify findings (severity, type, scope, complexity)
→ Detect patterns (3+ similar → framework issue)
→ Close audit issues, link to children
STAGE 2: ORGANISE (flow: create-epic)
Input: Implementation issues
Output: Epic parent with children, branch, phase ordering
→ Group by theme (security, quality, testing)
→ Order into phases (blockers → parallel → cleanup)
→ Create epic branch off dev
STAGE 3: EXECUTE (flow: issue-epic)
Input: Epic with children, branch
Output: Merged PRs, closed issues, training data
→ Dispatch Phase 1 to agents
→ Monitor: CI, reviews, conflicts, merges
→ Intervene: fix reviews / fix conflicts
→ Phase complete → dispatch next phase
→ Epic complete → merge epic branch to dev
```
## Gotchas (Battle-Tested)
| Gotcha | Fix |
|--------|-----|
| Jules creates PRs as user, not bot | Match by branch/issue linkage, not author |
| `git push origin dev` ambiguous (tag+branch) | Use `HEAD:refs/heads/dev` |
| Base branch gofmt breaks ALL PRs | Fix base first, not the PRs |
| Auto-merge needs explicit permissions in caller | Add `permissions: contents: write, pull-requests: write` |
| `--squash` conflicts with merge queue | Use `--auto` alone — queue controls strategy |
## Knowledge Accumulation (Discussions Strategy)
Non-actionable findings (nitpicks, patterns, style preferences) get posted to a queryable knowledge base (Forge/OpenBrain). When patterns emerge, humans create issues.
```
Build → Agents review → Actionable → Fix immediately
→ Non-actionable → Post to knowledge base
→ Patterns emerge
→ Human creates Issue
→ Agent picks up via pipeline
```
### Discussion Categories
| Channel | Category | Purpose |
|---------|----------|---------|
| 🚧 dev | PR build findings | Per-PR QA findings |
| 🛩️ alpha | Canary findings | Early testing |
| 🛸 beta | Integration findings | Integration testing |
| 🚀 stable | Release audit | Production audit |
### Naming: `{tool}:v{VERSION}`
`qa:v0.0.4.pr.264`, `lint:v0.0.4-alpha.42`, `audit:v0.0.4`
Tool prefixes: `qa:`, `lint:`, `static:`, `docker:`, `e2e:`, `perf:`, `security:`, `audit:`
### Pattern Detection
Query discussions to surface patterns across builds:
```bash
# 47 aria-label mentions across dev discussions → time for a11y audit issue
gh api graphql ... | grep -c "aria-label"
```
### CLI Integration
```bash
core go qa --post-findings # Post lint findings to discussion
core php qa --post-findings # Same for PHP
core qa # Aggregated summary
```
### Connection to Training
Discussion patterns → Issue → Agent implements → PR merged → findings captured as LEM training data. The feedback loop that makes agents better at conventions over time.
---
## Related RFCs
- `code/core/agent/flow/` — Flow YAML system
- `code/core/agent/RFC.md` — Agent dispatch system
- `project/lthn/lem/RFC-TRAINING-PIPELINE.md` — Findings → training data

65
docs/RFC-AGENT-PLAN.md Normal file
View file

@ -0,0 +1,65 @@
# RFC Plan — How to Start a core/agent Session
> For future Claude sessions. Do this FIRST before touching code.
## Step 1: Load the Domain
Read these files in order using ReadFile. Yes, all of them. The ~2000 tokens of boot cost pays for itself immediately — zero corrections, zero rediscovery.
```
1. ReadFile /Users/snider/Code/core/go/docs/RFC.md (1278 lines — core/go contract, 21 sections)
2. ReadFile /Users/snider/Code/core/agent/docs/RFC.md (~500 lines — core/agent contract, 22 sections)
3. ReadFile /Users/snider/Code/core/go-process/docs/RFC.md (~224 lines — go-process contract, 8 sections)
```
After loading all three, you have the full domain model:
- Every core/go primitive and how core/agent uses it
- The current state of core/agent (what's migrated, what isn't)
- The file layout with per-file migration actions
- The quality gates (10 disallowed imports, test naming, string concat)
- The completion pipeline architecture
- The entitlement/permission model
## Step 2: Verify Context
After loading, you should be able to answer without looking at code:
- What does `c.Action("agentic.dispatch").Run(ctx, opts)` do?
- Why is `proc.go` being deleted?
- What replaces the ACTION cascade in `handlers.go`?
- Which imports are disallowed and what replaces each one?
- What does `c.Entitled("agentic.concurrency", 1)` check?
If you can't answer these, re-read the RFCs.
## Step 3: Work the Migration
The core/agent RFC Section "Current State" has the annotated file layout. Each file is marked DELETE, REWRITE, or MIGRATE with the specific action.
Priority order:
1. `OnStartup`/`OnShutdown` return `Result` (breaking, do first)
2. Replace `unsafe.Pointer``Fs.NewUnrestricted()` (paths.go)
3. Replace `os.WriteFile``Fs.WriteAtomic` (status.go)
4. Replace `core.ValidateName` / `core.SanitisePath` (prep.go, plan.go)
5. Replace `core.ID()` (plan.go)
6. Register capabilities as named Actions (OnStartup)
7. Replace ACTION cascade with Task pipeline (handlers.go)
8. Delete `proc.go``s.Core().Process()` (after go-process v0.8.0)
9. AX-7 test rename + gap fill
10. Example tests per source file
## Step 4: Session Cadence
Follow the CLAUDE.md session cadence:
- **0-50%**: Build — implement the migration
- **50%**: Feature freeze — finish what's in progress
- **60%+**: Refine — review passes on RFC.md, docs, CLAUDE.md, llm.txt
- **80%+**: Save state — update RFCs with what shipped
## What NOT to Do
- Don't guess the architecture — it's in the RFCs
- Don't use `os`, `os/exec`, `fmt`, `errors`, `io`, `path/filepath`, `encoding/json`, `strings`, `log`, `unsafe` — Core has primitives for all of these
- Don't use string concat with `+` — use `core.Concat()` or `core.Path()`
- Don't add `fmt.Println` — use `core.Println()`
- Don't write anonymous closures in command registration — extract to named methods
- Don't nest `c.ACTION()` calls — use `c.Task()` composition

View file

@ -0,0 +1,117 @@
# Plugin Restructure: dappcore → core + API/MCP Integration
## Context
3 skeleton plugins (core-go, core-php, infra) need building out. The go-agent repo has 67 commands across 11 plugins that can enrich them. Plugins need configuring to work with `{api,mcp}.lthn.sh` endpoints (JSON via `Accept` header, default returns HTML).
## Step 1: Rename dappcore-go → core-go
**Files to modify:**
- `plugins/dappcore-go/.claude-plugin/plugin.json` — change name, update metadata
- Rename directory: `dappcore-go/``core-go/`
**Keep existing skills** (they're solid):
- `core/SKILL.md` — CLI reference & decision tree
- `core-go/SKILL.md` — Go framework patterns (pkg structure, CLI helpers, i18n, test naming)
- `go-agent/SKILL.md` — Autonomous dev workflow (7-step loop, PR management, CodeRabbit)
**Add from go-agent/claude/code:**
- `commands/qa.md` — QA fix loop (from code plugin, Go-specific)
- `commands/commit.md` — Smart conventional commit
- `commands/review.md` — Code review (from review plugin)
- `commands/verify.md` — Verification gate (from verify plugin)
**Add agents:**
- `agents/go-developer.md` — Go dev agent persona (derived from go-agent skill)
**Add:**
- `README.md`
- `marketplace.yaml` (template from agentic-flows)
## Step 2: Rename dappcore-php → core-php
**Files to modify:**
- `plugins/dappcore-php/.claude-plugin/plugin.json` — change name, update metadata
- Rename directory: `dappcore-php/``core-php/`
**Keep existing skills:**
- `core-php/SKILL.md` — Module structure, Boot class, Action pattern, multi-tenant
- `php-agent/SKILL.md` — Autonomous PHP dev workflow (TDD, CodeRabbit, issue loop)
**Add from go-agent/claude/code:**
- `commands/qa.md` — QA fix loop (PHP-specific: pest, pint, analyse)
- `commands/commit.md` — Smart conventional commit
- `commands/review.md` — Code review
- `commands/verify.md` — Verification gate
**Add agents:**
- `agents/php-developer.md` — PHP/Laravel dev agent persona
**Add:**
- `README.md`
- `marketplace.yaml`
## Step 3: Update infra plugin
**Keep existing skills** (content is detailed and good):
- `infra/SKILL.md` — Machine inventory, NOC services, network config
- `gitea/SKILL.md` — Forge/Forgejo CLI commands, org structure, mirrors
**Rename skill:** `agents/``brand/` (it's about Vi mascot & brand voice, not agent definitions)
**Add agents:**
- `agents/infra-ops.md` — Infrastructure operations agent
**Add from go-agent/claude/coolify:**
- `commands/deploy.md` — Service deployment
- `commands/status.md` — Deployment status check
**Add:**
- `README.md`
- `marketplace.yaml`
**Fix plugin.json:** Update skill references after rename
## Step 4: API/MCP endpoint configuration
Add a shared skill or pattern file that documents the endpoint convention for all plugins:
**Create `core-go/skills/api-endpoints/SKILL.md`** (and symlink or copy to core-php, infra):
Content covers:
- `api.lthn.sh` — REST API
- `mcp.lthn.sh` — MCP bridge endpoint
- **Must send `Accept: application/json`** — default returns HTML
- **Must send `Content-Type: application/json`** for POST bodies
- Auth: Bearer token in `Authorization` header
- REST convention: `/v1/{resource}`
- This is both OSS (people run their own lthn.sh) and production
**Update `.mcp.json`** in core-go and core-php to reference `core mcp serve` (same pattern as agentic-flows).
## Step 5: Add marketplace.yaml to all 3 plugins
Template from agentic-flows, adjusted per plugin:
```yaml
marketplace:
registry: forge.lthn.ai
organization: core
repository: {plugin-name}
auto_update: true
check_interval: 24h
```
## Verification
1. Check plugin structure matches convention: `.claude-plugin/plugin.json` at root, commands/agents/skills at root level
2. Validate all SKILL.md files have proper YAML frontmatter
3. Validate all command .md files have proper frontmatter with name/description
4. Confirm no hardcoded paths (use `${CLAUDE_PLUGIN_ROOT}` where needed)
5. Test that `core mcp serve` still works with updated .mcp.json configs
## Out of Scope
- lethean & cryptonote-archive plugins (reference material)
- go-agent/claude/ plugins (stay in Go repo, not merged into shared plugins)
- EaaS subsystem references (stripped for OSS release)
- Codex/Gemini plugins (stay in go-agent)

View file

@ -0,0 +1,68 @@
# Agent Fleet Topology
> How Cladius, Charon, and community agents are deployed, connected, and onboarded.
---
## Current Fleet
| Agent | Hardware | Location | Role |
|-------|----------|----------|------|
| Cladius | M3 Studio (36GB) | Local (Snider's desk) | Project leader, architecture, specs, dispatch |
| Charon | Ryzen 9 + 128GB + RX 7800 XT | Homelab (10.69.69.165) | Infrastructure, training, blockchain, DevOps |
| Codex agents | OpenAI cloud | Remote (sandboxed) | Implementation, polish, QA |
| Gemini agents | Google cloud | Remote | Research, analysis, alternative perspectives |
## Connectivity
```
Cladius (M3 Studio)
└── core-agent MCP (stdio) → Claude Code
└── agent_send → Charon (api.lthn.sh)
Charon (Homelab)
└── core-agent MCP (stdio) → Claude Code
└── agent_send → Cladius (api.lthn.sh)
└── Ollama (local inference)
└── Qdrant (OpenBrain vectors)
Both → OpenBrain (shared knowledge)
Both → Forge (git repos)
Both → api.lthn.sh / mcp.lthn.sh (MCP over HTTP)
```
## DNS Routing Strategy
Subdomains, not paths:
- `api.lthn.sh` — REST API
- `mcp.lthn.sh` — MCP endpoint
- `forge.lthn.ai` — Forgejo (de1 production)
Why subdomains: each service can have its own TLS cert, its own Traefik rule,
its own rate limiting. Paths create coupling.
## Community Onboarding (*.lthn.sh)
The `*.lthn.sh` wildcard resolves to 10.69.69.165 (homelab) for Snider,
but for community members it resolves to 127.0.0.1 (localhost).
This means:
1. Community member installs core-agent
2. core-agent starts local MCP server
3. `api.lthn.sh` resolves to their own localhost
4. They're running their own node — no dependency on Snider's hardware
5. When they're ready, they peer with the network via WireGuard
BugSETI bootstrap tool automates this: bare metal → running node in 10 steps.
## Fleet Dispatch (lthn.sh)
lthn.sh is the fleet controller:
1. Orchestrator creates task
2. Task assigned to agent pool (codex, gemini, claude, local)
3. Agent picks up via SSE/polling from api.lthn.sh
4. Runs in sandboxed workspace
5. Reports completion via checkin API
6. Orchestrator reviews, merges, or sends back
Community members contribute compute by running core-agent connected to the fleet.

1068
docs/RFC-AGENT.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,440 @@
# RFC-025: Agent Experience (AX) Design Principles
- **Status:** Draft
- **Authors:** Snider, Cladius
- **Date:** 2026-03-19
- **Applies to:** All Core ecosystem packages (CoreGO, CorePHP, CoreTS, core-agent)
## Abstract
Agent Experience (AX) is a design paradigm for software systems where the primary code consumer is an AI agent, not a human developer. AX sits alongside User Experience (UX) and Developer Experience (DX) as the third era of interface design.
This RFC establishes AX as a formal design principle for the Core ecosystem and defines the conventions that follow from it.
## Motivation
As of early 2026, AI agents write, review, and maintain the majority of code in the Core ecosystem. The original author has not manually edited code (outside of Core struct design) since October 2025. Code is processed semantically — agents reason about intent, not characters.
Design patterns inherited from the human-developer era optimise for the wrong consumer:
- **Short names** save keystrokes but increase semantic ambiguity
- **Functional option chains** are fluent for humans but opaque for agents tracing configuration
- **Error-at-every-call-site** produces 50% boilerplate that obscures intent
- **Generic type parameters** force agents to carry type context that the runtime already has
- **Panic-hiding conventions** (`Must*`) create implicit control flow that agents must special-case
AX acknowledges this shift and provides principles for designing code, APIs, file structures, and conventions that serve AI agents as first-class consumers.
## The Three Eras
| Era | Primary Consumer | Optimises For | Key Metric |
|-----|-----------------|---------------|------------|
| UX | End users | Discoverability, forgiveness, visual clarity | Task completion time |
| DX | Developers | Typing speed, IDE support, convention familiarity | Time to first commit |
| AX | AI agents | Predictability, composability, semantic navigation | Correct-on-first-pass rate |
AX does not replace UX or DX. End users still need good UX. Developers still need good DX. But when the primary code author and maintainer is an AI agent, the codebase should be designed for that consumer first.
## Principles
### 1. Predictable Names Over Short Names
Names are tokens that agents pattern-match across languages and contexts. Abbreviations introduce mapping overhead.
```
Config not Cfg
Service not Srv
Embed not Emb
Error not Err (as a subsystem name; err for local variables is fine)
Options not Opts
```
**Rule:** If a name would require a comment to explain, it is too short.
**Exception:** Industry-standard abbreviations that are universally understood (`HTTP`, `URL`, `ID`, `IPC`, `I18n`) are acceptable. The test: would an agent trained on any mainstream language recognise it without context?
### 2. Comments as Usage Examples
The function signature tells WHAT. The comment shows HOW with real values.
```go
// Detect the project type from files present
setup.Detect("/path/to/project")
// Set up a workspace with auto-detected template
setup.Run(setup.Options{Path: ".", Template: "auto"})
// Scaffold a PHP module workspace
setup.Run(setup.Options{Path: "./my-module", Template: "php"})
```
**Rule:** If a comment restates what the type signature already says, delete it. If a comment shows a concrete usage with realistic values, keep it.
**Rationale:** Agents learn from examples more effectively than from descriptions. A comment like "Run executes the setup process" adds zero information. A comment like `setup.Run(setup.Options{Path: ".", Template: "auto"})` teaches an agent exactly how to call the function.
### 3. Path Is Documentation
File and directory paths should be self-describing. An agent navigating the filesystem should understand what it is looking at without reading a README.
```
flow/deploy/to/homelab.yaml — deploy TO the homelab
flow/deploy/from/github.yaml — deploy FROM GitHub
flow/code/review.yaml — code review flow
template/file/go/struct.go.tmpl — Go struct file template
template/dir/workspace/php/ — PHP workspace scaffold
```
**Rule:** If an agent needs to read a file to understand what a directory contains, the directory naming has failed.
**Corollary:** The unified path convention (folder structure = HTTP route = CLI command = test path) is AX-native. One path, every surface.
### 4. Templates Over Freeform
When an agent generates code from a template, the output is constrained to known-good shapes. When an agent writes freeform, the output varies.
```go
// Template-driven — consistent output
lib.RenderFile("php/action", data)
lib.ExtractDir("php", targetDir, data)
// Freeform — variance in output
"write a PHP action class that..."
```
**Rule:** For any code pattern that recurs, provide a template. Templates are guardrails for agents.
**Scope:** Templates apply to file generation, workspace scaffolding, config generation, and commit messages. They do NOT apply to novel logic — agents should write business logic freeform with the domain knowledge available.
### 5. Declarative Over Imperative
Agents reason better about declarations of intent than sequences of operations.
```yaml
# Declarative — agent sees what should happen
steps:
- name: build
flow: tools/docker-build
with:
context: "{{ .app_dir }}"
image_name: "{{ .image_name }}"
- name: deploy
flow: deploy/with/docker
with:
host: "{{ .host }}"
```
```go
// Imperative — agent must trace execution
cmd := exec.Command("docker", "build", "--platform", "linux/amd64", "-t", imageName, ".")
cmd.Dir = appDir
if err := cmd.Run(); err != nil {
return fmt.Errorf("docker build: %w", err)
}
```
**Rule:** Orchestration, configuration, and pipeline logic should be declarative (YAML/JSON). Implementation logic should be imperative (Go/PHP/TS). The boundary is: if an agent needs to compose or modify the logic, make it declarative.
### 6. Universal Types (Core Primitives)
Every component in the ecosystem accepts and returns the same primitive types. An agent processing any level of the tree sees identical shapes.
```go
// Universal contract
setup.Run(core.Options{Path: ".", Template: "auto"})
brain.New(core.Options{Name: "openbrain"})
deploy.Run(core.Options{Flow: "deploy/to/homelab"})
// Fractal — Core itself is a Service
core.New(core.Options{
Services: []core.Service{
process.New(core.Options{Name: "process"}),
brain.New(core.Options{Name: "brain"}),
},
})
```
**Core primitive types:**
| Type | Purpose |
|------|---------|
| `core.Options` | Input configuration (what you want) |
| `core.Config` | Runtime settings (what is active) |
| `core.Data` | Embedded or stored content |
| `core.Service` | A managed component with lifecycle |
| `core.Result[T]` | Return value with OK/fail state |
**What this replaces:**
| Go Convention | Core AX | Why |
|--------------|---------|-----|
| `func With*(v) Option` | `core.Options{Field: v}` | Struct literal is parseable; option chain requires tracing |
| `func Must*(v) T` | `core.Result[T]` | No hidden panics; errors flow through Core |
| `func *For[T](c) T` | `c.Service("name")` | String lookup is greppable; generics require type context |
| `val, err :=` everywhere | Single return via `core.Result` | Intent not obscured by error handling |
| `_ = err` | Never needed | Core handles all errors internally |
### 7. Directory as Semantics
The directory structure tells an agent the intent before it reads a word. Top-level directories are semantic categories, not organisational bins.
```
plans/
├── code/ # Pure primitives — read for WHAT exists
├── project/ # Products — read for WHAT we're building and WHY
└── rfc/ # Contracts — read for constraints and rules
```
**Rule:** An agent should know what kind of document it's reading from the path alone. `code/core/go/io/RFC.md` = a lib primitive spec. `project/ofm/RFC.md` = a product spec that cross-references code/. `rfc/snider/borg/RFC-BORG-006-SMSG-FORMAT.md` = an immutable contract for the Borg SMSG protocol.
**Corollary:** The three-way split (code/project/rfc) extends principle 3 (Path Is Documentation) from files to entire subtrees. The path IS the metadata.
### 8. Lib Never Imports Consumer
Dependency flows one direction. Libraries define primitives. Consumers compose from them. A new feature in a consumer can never break a library.
```
code/core/go/* → lib tier (stable foundation)
code/core/agent/ → consumer tier (composes from go/*)
code/core/cli/ → consumer tier (composes from go/*)
code/core/gui/ → consumer tier (composes from go/*)
```
**Rule:** If package A is in `go/` and package B is in the consumer tier, B may import A but A must never import B. The repo naming convention enforces this: `go-{name}` = lib, bare `{name}` = consumer.
**Why this matters for agents:** When an agent is dispatched to implement a feature in `core/agent`, it can freely import from `go-io`, `go-scm`, `go-process`. But if an agent is dispatched to `go-io`, it knows its changes are foundational — every consumer depends on it, so the contract must not break.
### 9. Issues Are N+(rounds) Deep
Problems in code and specs are layered. Surface issues mask deeper issues. Fixing the surface reveals the next layer. This is not a failure mode — it is the discovery process.
```
Pass 1: Find 16 issues (surface — naming, imports, obvious errors)
Pass 2: Find 11 issues (structural — contradictions, missing types)
Pass 3: Find 5 issues (architectural — signature mismatches, registration gaps)
Pass 4: Find 4 issues (contract — cross-spec API mismatches)
Pass 5: Find 2 issues (mechanical — path format, nil safety)
Pass N: Findings are trivial → spec/code is complete
```
**Rule:** Iteration is required, not a failure. Each pass sees what the previous pass could not, because the context changed. An agent dispatched with the same task on the same repo will find different things each time — this is correct behaviour.
**Corollary:** The cheapest model should do the most passes (surface work). The frontier model should arrive last, when only deep issues remain. Tiered iteration: grunt model grinds → mid model pre-warms → frontier model polishes.
**Anti-pattern:** One-shot generation expecting valid output. No model, no human, produces correct-on-first-pass for non-trivial work. Expecting it wastes the first pass on surface issues that a cheaper pass would have caught.
### 10. CLI Tests as Artifact Validation
Unit tests verify the code. CLI tests verify the binary. The directory structure IS the command structure — path maps to command, Taskfile runs the test.
```
tests/cli/
├── core/
│ └── lint/
│ ├── Taskfile.yaml ← test `core-lint` (root)
│ ├── run/
│ │ ├── Taskfile.yaml ← test `core-lint run`
│ │ └── fixtures/
│ ├── go/
│ │ ├── Taskfile.yaml ← test `core-lint go`
│ │ └── fixtures/
│ └── security/
│ ├── Taskfile.yaml ← test `core-lint security`
│ └── fixtures/
```
**Rule:** Every CLI command has a matching `tests/cli/{path}/Taskfile.yaml`. The Taskfile runs the compiled binary against fixtures with known inputs and validates the output. If the CLI test passes, the underlying actions work — because CLI commands call actions, MCP tools call actions, API endpoints call actions. Test the CLI, trust the rest.
**Pattern:**
```yaml
# tests/cli/core/lint/go/Taskfile.yaml
version: '3'
tasks:
test:
cmds:
- core-lint go --output json fixtures/ > /tmp/result.json
- jq -e '.findings | length > 0' /tmp/result.json
- jq -e '.summary.passed == false' /tmp/result.json
```
**Why this matters for agents:** An agent can validate its own work by running `task test` in the matching `tests/cli/` directory. No test framework, no mocking, no setup — just the binary, fixtures, and `jq` assertions. The agent builds the binary, runs the test, sees the result. If it fails, the agent can read the fixture, read the output, and fix the code.
**Corollary:** Fixtures are planted bugs. Each fixture file has a known issue that the linter must find. If the linter doesn't find it, the test fails. Fixtures are the spec for what the tool must detect — they ARE the test cases, not descriptions of test cases.
## Applying AX to Existing Patterns
### File Structure
```
# AX-native: path describes content
core/agent/
├── go/ # Go source
├── php/ # PHP source
├── ui/ # Frontend source
├── claude/ # Claude Code plugin
└── codex/ # Codex plugin
# Not AX: generic names requiring README
src/
├── lib/
├── utils/
└── helpers/
```
### Error Handling
```go
// AX-native: errors are infrastructure, not application logic
svc := c.Service("brain")
cfg := c.Config().Get("database.host")
// Errors logged by Core. Code reads like a spec.
// Not AX: errors dominate the code
svc, err := c.ServiceFor[brain.Service]()
if err != nil {
return fmt.Errorf("get brain service: %w", err)
}
cfg, err := c.Config().Get("database.host")
if err != nil {
_ = err // silenced because "it'll be fine"
}
```
### API Design
```go
// AX-native: one shape, every surface
core.New(core.Options{
Name: "my-app",
Services: []core.Service{...},
Config: core.Config{...},
})
// Not AX: multiple patterns for the same thing
core.New(
core.WithName("my-app"),
core.WithService(factory1),
core.WithService(factory2),
core.WithConfig(cfg),
)
```
## The Plans Convention — AX Development Lifecycle
The `plans/` directory structure encodes a development methodology designed for how generative AI actually works: iterative refinement across structured phases, not one-shot generation.
### The Three-Way Split
```
plans/
├── project/ # 1. WHAT and WHY — start here
├── rfc/ # 2. CONSTRAINTS — immutable contracts
└── code/ # 3. HOW — implementation specs
```
Each directory is a phase. Work flows from project → rfc → code. Each transition forces a refinement pass — you cannot write a code spec without discovering gaps in the project spec, and you cannot write an RFC without discovering assumptions in both.
**Three places for data that can't be written simultaneously = three guaranteed iterations of "actually, this needs changing."** Refinement is baked into the structure, not bolted on as a review step.
### Phase 1: Project (Vision)
Start with `project/`. No code exists yet. Define:
- What the product IS and who it serves
- What existing primitives it consumes (cross-ref to `code/`)
- What constraints it operates under (cross-ref to `rfc/`)
This is where creativity lives. Map features to building blocks. Connect systems. The project spec is integrative — it references everything else.
### Phase 2: RFC (Contracts)
Extract the immutable rules into `rfc/`. These are constraints that don't change with implementation:
- Wire formats, protocols, hash algorithms
- Security properties that must hold
- Compatibility guarantees
RFCs are numbered per component (`RFC-BORG-006-SMSG-FORMAT.md`) and never modified after acceptance. If the contract changes, write a new RFC.
### Phase 3: Code (Implementation Specs)
Define the implementation in `code/`. Each component gets an RFC.md that an agent can implement from:
- Struct definitions (the DTOs — see principle 6)
- Method signatures and behaviour
- Error conditions and edge cases
- Cross-references to other code/ specs
The code spec IS the product. Write the spec → dispatch to an agent → review output → iterate.
### Pre-Launch: Alignment Protocol
Before dispatching for implementation, verify spec-model alignment:
```
1. REVIEW — The implementation model (Codex/Jules) reads the spec
and reports missing elements. This surfaces the delta between
the model's training and the spec's assumptions.
"I need X, Y, Z to implement this" is the model saying
"I hear you but I'm missing context" — without asking.
2. ADJUST — Update the spec to close the gaps. Add examples,
clarify ambiguities, provide the context the model needs.
This is shared alignment, not compromise.
3. VERIFY — A different model (or sub-agent) reviews the adjusted
spec without the planner's bias. Fresh eyes on the contract.
"Does this make sense to someone who wasn't in the room?"
4. READY — When the review findings are trivial or deployment-
related (not architectural), the spec is ready to dispatch.
```
### Implementation: Iterative Dispatch
Same prompt, multiple runs. Each pass sees deeper because the context evolved:
```
Round 1: Build features (the obvious gaps)
Round 2: Write tests (verify what was built)
Round 3: Harden security (what can go wrong?)
Round 4: Next RFC section (what's still missing?)
Round N: Findings are trivial → implementation is complete
```
Re-running is not failure. It is the process. Each pass changes the codebase, which changes what the next pass can see. The iteration IS the refinement.
### Post-Implementation: Auto-Documentation
The QA/verify chain produces artefacts that feed forward:
- Test results document the contract (what works, what doesn't)
- Coverage reports surface untested paths
- Diff summaries prep the changelog for the next release
- Doc site updates from the spec (the spec IS the documentation)
The output of one cycle is the input to the next. The plans repo stays current because the specs drive the code, not the other way round.
## Compatibility
AX conventions are valid, idiomatic Go/PHP/TS. They do not require language extensions, code generation, or non-standard tooling. An AX-designed codebase compiles, tests, and deploys with standard toolchains.
The conventions diverge from community patterns (functional options, Must/For, etc.) but do not violate language specifications. This is a style choice, not a fork.
## Adoption
AX applies to all new code in the Core ecosystem. Existing code migrates incrementally as it is touched — no big-bang rewrite.
Priority order:
1. **Public APIs** (package-level functions, struct constructors)
2. **File structure** (path naming, template locations)
3. **Internal fields** (struct field names, local variables)
## References
- dAppServer unified path convention (2024)
- CoreGO DTO pattern refactor (2026-03-18)
- Core primitives design (2026-03-19)
- Go Proverbs, Rob Pike (2015) — AX provides an updated lens
## Changelog
- 2026-03-19: Initial draft

View file

@ -0,0 +1,75 @@
# core-agent — Commands
> CLI commands and MCP tool registrations.
## CLI Commands
```
core-agent [command]
```
| Command | Purpose |
|---------|---------|
| `version` | Print version |
| `check` | Health check |
| `env` | Show environment |
| `run/task` | Run a single agent task |
| `run/orchestrator` | Run the orchestrator daemon |
| `prep` | Prepare workspace without spawning |
| `status` | Show workspace status |
| `prompt` | Build/preview agent prompt |
| `extract` | Extract data from agent output |
| `workspace/list` | List agent workspaces |
| `workspace/clean` | Clean completed/failed workspaces |
| `workspace/dispatch` | Dispatch agent to workspace |
| `issue/get` | Get Forge issue by number |
| `issue/list` | List Forge issues |
| `issue/comment` | Comment on Forge issue |
| `issue/create` | Create Forge issue |
| `pr/get` | Get Forge PR by number |
| `pr/list` | List Forge PRs |
| `pr/merge` | Merge Forge PR |
| `repo/get` | Get Forge repo info |
| `repo/list` | List Forge repos |
| `mcp` | Start MCP server (stdio) |
| `serve` | Start HTTP/API server |
## MCP Tools (via `core-agent mcp`)
### agentic (PrepSubsystem.RegisterTools)
- `agentic_dispatch` — dispatch a subagent to a sandboxed workspace
- `agentic_prep_workspace` — prepare workspace without spawning
- `agentic_status` — list agent workspaces and their status
- `agentic_watch` — watch running agents until completion
- `agentic_resume` — resume a blocked agent
- `agentic_review_queue` — review completed workspaces
- `agentic_scan` — scan Forge for actionable issues
- `agentic_mirror` — mirror repos between remotes
- `agentic_plan_create` / `plan_read` / `plan_update` / `plan_delete` / `plan_list`
- `agentic_create_pr` — create PR from agent workspace
- `agentic_create_epic` — create epic with child issues
- `agentic_dispatch_start` / `dispatch_shutdown` / `dispatch_shutdown_now`
- `agentic_dispatch_remote` / `agentic_status_remote`
### brain (DirectSubsystem.RegisterTools)
- `brain_recall` — search OpenBrain memories
- `brain_remember` — store a memory
- `brain_forget` — remove a memory
### brain (DirectSubsystem.RegisterMessagingTools)
- `agent_send` — send message to another agent
- `agent_inbox` — check incoming messages
- `agent_conversation` — view conversation history
### monitor (Subsystem.RegisterTools)
- Exposes agent workspace status as MCP resource
### File operations (via core-mcp)
- `file_read` / `file_write` / `file_edit` / `file_delete` / `file_rename` / `file_exists`
- `dir_list` / `dir_create`
- `lang_detect` / `lang_list`

View file

@ -0,0 +1,29 @@
# agent — Imports
> Ecosystem dependencies extracted from source code.
## dappco.re (migrated)
```
dappco.re/go/agent/pkg/agentic
dappco.re/go/agent/pkg/brain
dappco.re/go/agent/pkg/lib
dappco.re/go/agent/pkg/messages
dappco.re/go/agent/pkg/monitor
dappco.re/go/agent/pkg/runner
dappco.re/go/core
dappco.re/go/core/api
dappco.re/go/core/api/pkg/provider
dappco.re/go/core/forge
dappco.re/go/core/forge/types
dappco.re/go/core/process
dappco.re/go/core/ws
dappco.re/go/mcp/pkg/mcp
dappco.re/go/mcp/pkg/mcp/ide
```
## forge.lthn.ai
```
forge.lthn.ai/core/go-ws
```

1416
docs/RFC-GO-AGENT-MODELS.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
# core/agent — Agentic Orchestration
`dappco.re/go/agent` — The agent dispatch, monitoring, and fleet management system.
## Status
- **Version:** v0.10.0-alpha.1
- **RFC:** `code/core/agent/docs/RFC.md` + `code/core/agent/docs/RFC.plan.md`
- **Tests:** 8 packages, all passing
- **Binary:** `core-agent` (MCP server + CLI)
## What It Does
core-agent is both a binary (`core-agent`) and a library. It provides:
- **MCP server** — stdio transport, tool registration, channel notifications
- **Dispatch** — prep workspaces, spawn codex/claude/gemini agents in Docker
- **Runner service** — concurrency limits, queue drain, frozen state
- **Monitor** — background check loop, completion detection, inbox polling
- **Brain** — OpenBrain integration (recall, remember, forget)
- **Messaging** — agent-to-agent messages via lthn.sh API
## Architecture
```
cmd/core-agent/main.go
├── agentic.Register ← workspace prep, dispatch, MCP tools
├── runner.Register ← concurrency, queue drain, frozen state
├── monitor.Register ← background checks, channel notifications
├── brain.Register ← OpenBrain tools
└── mcp.Register ← MCP server + ChannelPush
```
Services communicate via Core IPC:
- `AgentStarted` → runner pushes ChannelPush → MCP sends to Claude Code
- `AgentCompleted` → runner updates Registry + pokes queue + ChannelPush
- `ChannelPush` → MCP HandleIPCEvents → ChannelSend to stdout

498
docs/RFC-GO-AGENT.md Normal file
View file

@ -0,0 +1,498 @@
# core/go/agent RFC — Go Agent Implementation
> The Go implementation of the agent system — dispatch, workspace management, MCP server.
> Implements `code/core/agent/RFC.md` contract in Go.
> An agent should be able to implement the Go agent from this document alone.
**Module:** `dappco.re/go/agent`
**Binary:** `~/.local/bin/core-agent`
**Depends on:** core/go v0.8.0, go-process v0.8.0
**Sub-specs:** [Models](RFC.models.md) | [Commands](RFC.commands.md)
---
## 1. Overview
core/go/agent is the local MCP server binary that dispatches AI agents, manages sandboxed workspaces, provides semantic memory (OpenBrain), and runs the completion pipeline. It composes core/go primitives (ServiceRuntime, Actions, Tasks, IPC, Process) into a single binary: `core-agent`.
The cross-cutting contract lives in `code/core/agent/RFC.md`. This document covers Go-specific patterns: service registration, named actions, process execution, status management, monitoring, MCP tools, runner service, dispatch routing, and quality gates.
---
## 2. Service Registration
All services use `ServiceRuntime[T]` — no raw `core *core.Core` fields.
```go
func Register(c *core.Core) core.Result {
prep := NewPrep()
prep.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
cfg := prep.loadAgentsConfig()
c.Config().Set("agents.concurrency", cfg.Concurrency)
c.Config().Set("agents.rates", cfg.Rates)
RegisterHandlers(c, prep)
return core.Result{Value: prep, OK: true}
}
// In main:
c := core.New(
core.WithService(process.Register),
core.WithService(agentic.Register),
core.WithService(brain.Register),
core.WithService(monitor.Register),
core.WithService(mcp.Register),
)
c.Run()
```
All subsystems embed `*core.ServiceRuntime[T]`:
```go
// pkg/agentic/ — PrepSubsystem
type PrepSubsystem struct {
*core.ServiceRuntime[AgentOptions]
}
// pkg/brain/ — BrainService
type BrainService struct {
*core.ServiceRuntime[BrainOptions]
}
// pkg/monitor/ — Monitor
type Monitor struct {
*core.ServiceRuntime[MonitorOptions]
}
// pkg/setup/ — Setup Service
type Service struct {
*core.ServiceRuntime[SetupOptions]
}
```
---
## 3. Named Actions
All capabilities registered as named Actions during OnStartup. Inspectable, composable, gatable by Entitlements.
```go
func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result {
c := s.Core()
// Dispatch & workspace
c.Action("agentic.dispatch", s.handleDispatch)
c.Action("agentic.prep", s.handlePrep)
c.Action("agentic.status", s.handleStatus)
c.Action("agentic.resume", s.handleResume)
c.Action("agentic.scan", s.handleScan)
c.Action("agentic.watch", s.handleWatch)
// Pipeline
c.Action("agentic.qa", s.handleQA)
c.Action("agentic.auto-pr", s.handleAutoPR)
c.Action("agentic.verify", s.handleVerify)
c.Action("agentic.ingest", s.handleIngest)
c.Action("agentic.poke", s.handlePoke)
c.Action("agentic.mirror", s.handleMirror)
// Forge
c.Action("agentic.issue.get", s.handleIssueGet)
c.Action("agentic.issue.list", s.handleIssueList)
c.Action("agentic.issue.create", s.handleIssueCreate)
c.Action("agentic.pr.get", s.handlePRGet)
c.Action("agentic.pr.list", s.handlePRList)
c.Action("agentic.pr.merge", s.handlePRMerge)
// Review & Epic
c.Action("agentic.review-queue", s.handleReviewQueue)
c.Action("agentic.epic", s.handleEpic)
// Completion pipeline — Task composition
c.Task("agent.completion", core.Task{
Description: "QA -> PR -> Verify -> Merge",
Steps: []core.Step{
{Action: "agentic.qa"},
{Action: "agentic.auto-pr"},
{Action: "agentic.verify"},
{Action: "agentic.ingest", Async: true},
{Action: "agentic.poke", Async: true},
},
})
s.StartRunner()
s.registerCommands(ctx)
s.registerWorkspaceCommands()
s.registerForgeCommands()
return core.Result{OK: true}
}
```
### Entitlement Gating
Actions are gated by `c.Entitled()` — checked automatically in `Action.Run()`:
```go
func (s *PrepSubsystem) handleDispatch(ctx context.Context, opts core.Options) core.Result {
e := s.Core().Entitled("agentic.concurrency", 1)
if !e.Allowed {
return core.Result{Value: core.E("dispatch", e.Reason, nil), OK: false}
}
// ... dispatch agent ...
s.Core().RecordUsage("agentic.dispatch")
return core.Result{OK: true}
}
```
### Remote Dispatch
Transparent local/remote via `host:action` syntax:
```go
r := c.RemoteAction("agentic.status", ctx, opts) // local
r := c.RemoteAction("charon:agentic.dispatch", ctx, opts) // remote
r := c.RemoteAction("snider.lthn:brain.recall", ctx, opts) // web3
```
### MCP Auto-Exposure
MCP auto-exposes all registered Actions as tools via `c.Actions()`. Register an Action and it appears as an MCP tool. The API stream primitive (`c.API()`) handles transport.
---
## 4. Package Structure
```
cmd/core-agent/main.go — entry point: core.New + Run
pkg/agentic/ — orchestration (dispatch, prep, verify, scan, commands)
pkg/agentic/actions.go — named Action handlers (ctx, Options) -> Result
pkg/agentic/proc.go — process helpers via s.Core().Process()
pkg/agentic/handlers.go — IPC completion pipeline handlers
pkg/agentic/status.go — workspace status (WriteAtomic + JSONMarshalString)
pkg/agentic/paths.go — paths, fs (NewUnrestricted), helpers
pkg/agentic/dispatch.go — agent dispatch logic
pkg/agentic/prep.go — workspace preparation
pkg/agentic/scan.go — Forge scanning for work
pkg/agentic/epic.go — epic creation
pkg/agentic/pr.go — pull request management
pkg/agentic/plan.go — plan CRUD
pkg/agentic/queue.go — dispatch queue
pkg/agentic/runner.go — runner service (concurrency, drain)
pkg/agentic/verify.go — output verification
pkg/agentic/watch.go — workspace watcher
pkg/agentic/resume.go — session resumption
pkg/agentic/review_queue.go — review queue management
pkg/agentic/mirror.go — remote mirroring
pkg/agentic/remote.go — remote dispatch
pkg/agentic/shutdown.go — graceful shutdown
pkg/agentic/events.go — event definitions
pkg/agentic/transport.go — Forgejo HTTP client (one file)
pkg/agentic/commands.go — CLI command registration
pkg/brain/ — OpenBrain (recall, remember, search)
pkg/brain/brain.go — brain service
pkg/brain/direct.go — direct API calls
pkg/brain/messaging.go — agent-to-agent messaging
pkg/brain/provider.go — embedding provider
pkg/brain/register.go — service registration
pkg/brain/tools.go — MCP tool handlers
pkg/lib/ — embedded templates, personas, flows, plans
pkg/messages/ — typed message structs for IPC broadcast
pkg/monitor/ — agent monitoring via IPC (ServiceRuntime)
pkg/setup/ — workspace detection + scaffolding (Service)
claude/ — Claude Code plugin definitions
docs/ — RFC, plans, architecture
```
---
## 5. Process Execution
All commands via `s.Core().Process()`. Returns `core.Result` — Value is always a string.
```go
func (s *PrepSubsystem) runCmd(ctx context.Context, dir, command string, args ...string) core.Result {
return s.Core().Process().RunIn(ctx, dir, command, args...)
}
func (s *PrepSubsystem) runCmdOK(ctx context.Context, dir, command string, args ...string) bool {
return s.runCmd(ctx, dir, command, args...).OK
}
func (s *PrepSubsystem) gitCmd(ctx context.Context, dir string, args ...string) core.Result {
return s.runCmd(ctx, dir, "git", args...)
}
func (s *PrepSubsystem) gitOutput(ctx context.Context, dir string, args ...string) string {
r := s.gitCmd(ctx, dir, args...)
if !r.OK { return "" }
return core.Trim(r.Value.(string))
}
```
go-process is fully Result-native. `Start`, `Run`, `StartWithOptions`, `RunWithOptions` all return `core.Result`. Value is `*Process` for Start, `string` for Run. OK=true guarantees the type.
---
## 6. Status Management
Workspace status uses `WriteAtomic` + `JSONMarshalString` for safe concurrent access:
```go
func writeStatus(wsDir string, status *WorkspaceStatus) error {
status.UpdatedAt = time.Now()
statusPath := core.JoinPath(wsDir, "status.json")
if r := fs.WriteAtomic(statusPath, core.JSONMarshalString(status)); !r.OK {
err, _ := r.Value.(error)
return core.E("writeStatus", "failed to write status", err)
}
return nil
}
```
### Registry for Workspace Tracking
```go
workspaces := core.NewRegistry[*WorkspaceStatus]()
workspaces.Set(wsDir, status)
workspaces.Get(wsDir)
workspaces.Each(func(dir string, st *WorkspaceStatus) { ... })
workspaces.Names() // insertion order
c.RegistryOf("actions").List("agentic.*")
```
### Filesystem
Package-level unrestricted Fs via Core primitive:
```go
var fs = (&core.Fs{}).NewUnrestricted()
```
---
## 7. Monitor Service
Embeds `*core.ServiceRuntime[MonitorOptions]`. All notifications via `m.Core().ACTION(messages.X{})` — no ChannelNotifier interface. Git operations via `m.Core().Process()`.
```go
func Register(c *core.Core) core.Result {
mon := New()
mon.ServiceRuntime = core.NewServiceRuntime(c, MonitorOptions{})
c.RegisterAction(func(c *core.Core, msg core.Message) core.Result {
switch ev := msg.(type) {
case messages.AgentCompleted:
mon.handleAgentCompleted(ev)
case messages.AgentStarted:
mon.handleAgentStarted(ev)
}
return core.Result{OK: true}
})
return core.Result{Value: mon, OK: true}
}
```
### IPC Completion Pipeline
Registered in `RegisterHandlers()`:
```
AgentCompleted -> QA handler -> QAResult
QAResult{Passed} -> PR handler -> PRCreated
PRCreated -> Verify handler -> PRMerged | PRNeedsReview
AgentCompleted -> Ingest handler (findings -> issues)
AgentCompleted -> Poke handler (drain queue)
```
All handlers use `c.ACTION(messages.X{})` — no ChannelNotifier, no callbacks.
---
## 8. MCP Tools
25+ tools registered via named Actions:
### Dispatch
`agentic_dispatch`, `agentic_status`, `agentic_scan`, `agentic_watch`, `agentic_resume`, `agentic_review_queue`, `agentic_dispatch_start`, `agentic_dispatch_shutdown`
### Workspace
`agentic_prep_workspace`, `agentic_create_epic`, `agentic_create_pr`, `agentic_list_prs`, `agentic_mirror`
### Plans
`agentic_plan_create`, `agentic_plan_read`, `agentic_plan_update`, `agentic_plan_list`, `agentic_plan_delete`
### Brain
`brain_remember`, `brain_recall`, `brain_forget`
### Messaging
`agent_send`, `agent_inbox`, `agent_conversation`
---
## 9. Runner Service
Owns dispatch concurrency (from `agents.yaml` config) and queue drain.
- Checks concurrency limits (total + per-model) before dispatching
- Checks rate limits (daily, min_delay, burst window)
- Pops next queued task matching an available pool
- Spawns agent in sandboxed workspace
- Channel notifications: `AgentStarted`/`AgentCompleted` push to Claude Code sessions
---
## 10. Dispatch and Pool Routing
### agents.yaml
See `code/core/agent/RFC.md` section "Configuration" for the full agents.yaml schema.
Go loads this config during `Register()`:
```go
cfg := prep.loadAgentsConfig()
c.Config().Set("agents.concurrency", cfg.Concurrency)
c.Config().Set("agents.rates", cfg.Rates)
```
### Configuration Access
```go
c.Config().Set("agents.concurrency", 5)
c.Config().String("workspace.root")
c.Config().Int("agents.concurrency")
c.Config().Enable("auto-merge")
if c.Config().Enabled("auto-merge") { ... }
```
### Workspace Prep by Language
- **Go**: `go mod download`, `go work sync`
- **PHP**: `composer install`
- **TypeScript**: `npm install`
- Language-specific CODEX.md generation from RFC
---
## 11. Quality Gates
### Banned Imports
Source files (not tests) must not import these — Core provides alternatives:
| Banned | Replacement |
|--------|-------------|
| `"os"` | `core.Env`, `core.Fs` |
| `"os/exec"` | `s.Core().Process()` |
| `"io"` | `core.ReadAll`, `core.WriteAll` |
| `"fmt"` | `core.Println`, `core.Sprintf`, `core.Concat` |
| `"errors"` | `core.E()` |
| `"log"` | `core.Info`, `core.Error`, `core.Security` |
| `"encoding/json"` | `core.JSONMarshalString`, `core.JSONUnmarshalString` |
| `"path/filepath"` | `core.JoinPath`, `core.Path` |
| `"unsafe"` | (never) |
| `"strings"` | `core.Contains`, `core.Split`, `core.Trim` |
Verification:
```bash
grep -rn '"os"\|"os/exec"\|"io"\|"fmt"\|"errors"\|"log"\|"encoding/json"\|"path/filepath"\|"unsafe"\|"strings"' *.go **/*.go \
| grep -v _test.go
```
### Error Handling
All errors via `core.E()`. All logging via Core:
```go
return core.E("dispatch.prep", "workspace not found", nil)
return core.E("dispatch.prep", core.Concat("repo ", repo, " invalid"), cause)
core.Info("agent dispatched", "repo", repo, "agent", agent)
core.Error("dispatch failed", "err", err)
core.Security("entitlement.denied", "action", action, "reason", reason)
```
### String Operations
No `fmt`, no `strings`, no `+` concat:
```go
core.Println(value) // not fmt.Println
core.Sprintf("port: %d", port) // not fmt.Sprintf
core.Concat("hello ", name) // not "hello " + name
core.Path(dir, "status.json") // not dir + "/status.json"
core.Contains(s, "prefix") // not strings.Contains
core.Split(s, "/") // not strings.Split
core.Trim(s) // not strings.TrimSpace
```
### JSON Serialisation
All JSON via Core primitives:
```go
data := core.JSONMarshalString(status)
core.JSONUnmarshalString(jsonStr, &result)
```
### Validation and IDs
```go
if r := core.ValidateName(input.Repo); !r.OK { return r }
safe := core.SanitisePath(userInput)
id := core.ID() // "id-42-a3f2b1"
```
### Stream Helpers and Data
```go
r := c.Data().ReadString("prompts/coding.md")
c.Data().List("templates/")
c.Drive().New(core.NewOptions(
core.Option{Key: "name", Value: "charon"},
core.Option{Key: "transport", Value: "http://10.69.69.165:9101"},
))
```
### Comments (AX Principle 2)
Every exported function MUST have a usage-example comment:
```go
// gitCmd runs a git command in a directory.
//
// r := s.gitCmd(ctx, "/repo", "log", "--oneline")
func (s *PrepSubsystem) gitCmd(ctx context.Context, dir string, args ...string) core.Result {
```
### Test Strategy (AX Principle 7)
`TestFile_Function_{Good,Bad,Ugly}` — 100% naming compliance target.
Verification:
```bash
grep -rn "^func Test" *_test.go **/*_test.go \
| grep -v "Test[A-Z][a-z]*_.*_\(Good\|Bad\|Ugly\)"
```
---
## 12. Reference Material
| Resource | Location |
|----------|----------|
| Agent contract (cross-cutting) | `code/core/agent/RFC.md` |
| Core framework spec | `code/core/go/RFC.md` |
| Process primitives | `code/core/go/process/RFC.md` |
| MCP spec | `code/core/mcp/RFC.md` |
| PHP implementation | `code/core/php/agent/RFC.md` |
---
## Changelog
- 2026-03-29: Restructured as Go implementation spec. Language-agnostic contract moved to `code/core/agent/RFC.md`. Retained all Go-specific patterns (ServiceRuntime, core.E, banned imports, AX principles).
- 2026-03-27: Initial Go agent RFC with MCP tools, runner service, fleet mode, polyglot mapping.

View file

@ -0,0 +1,226 @@
---
name: flow-audit-issues
description: Use when processing [Audit] issues to create implementation issues. Converts security/quality audit findings into actionable child issues for agent dispatch.
---
# Flow: Audit Issues
Turn audit findings into actionable implementation issues. Every finding matters — even nitpicks hint at framework-level patterns.
---
## Philosophy
> Every audit finding is valid. No dismissing, no "won't fix".
An agent found it for a reason. Even if the individual fix seems trivial, it may:
- Reveal a **pattern** across the codebase (10 similar issues = framework change)
- Become **training data** (good responses teach future models; bad responses go in the "bad responses" set — both have value)
- Prevent a **real vulnerability** that looks minor in isolation
Label accurately. Let the data accumulate. Patterns emerge from volume.
## When to Use
- An audit issue exists (e.g. `[Audit] OWASP Top 10`, `audit: Error handling`)
- The audit contains findings that need implementation work
- You need to convert audit prose into discrete, assignable issues
## Inputs
- **Audit issue**: The `[Audit]` or `audit:` issue with findings
- **Repo**: Where the audit was performed
## Process
### Step 1: Read the Audit
Read the audit issue body. It contains findings grouped by category/severity.
```bash
gh issue view AUDIT_NUMBER --repo OWNER/REPO
```
### Step 2: Classify Each Finding
For each finding, determine:
| Field | Values | Purpose |
|-------|--------|---------|
| **Severity** | `critical`, `high`, `medium`, `low` | Priority ordering |
| **Type** | `security`, `quality`, `performance`, `testing`, `docs` | Categorisation |
| **Scope** | `single-file`, `package`, `framework` | Size of fix |
| **Complexity** | `small`, `medium`, `large` | Agent difficulty |
### Scope Matters Most
| Scope | What it means | Example |
|-------|---------------|---------|
| `single-file` | Fix in one file, no API changes | Add input validation to one handler |
| `package` | Fix across a package, internal API may change | Add error wrapping throughout pkg/mcp |
| `framework` | Requires core abstraction change, affects many packages | Add centralised input sanitisation middleware |
**Nitpicky single-file issues that repeat across packages → framework scope.** The individual finding is small but the pattern is big. Create both:
1. Individual issues for each occurrence (labelled `single-file`)
2. A framework issue that solves all of them at once (labelled `framework`)
The framework issue becomes a blocker in an epic. The individual issues become children that validate the framework fix works.
### Step 3: Create Implementation Issues
One issue per finding. Use consistent title format.
```bash
gh issue create --repo OWNER/REPO \
--title "TYPE(PACKAGE): DESCRIPTION" \
--label "SEVERITY,TYPE,complexity:SIZE,SCOPE" \
--body "$(cat <<'EOF'
Parent audit: #AUDIT_NUMBER
## Finding
WHAT_THE_AUDIT_FOUND
## Location
- `path/to/file.go:LINE`
## Fix
WHAT_NEEDS_TO_CHANGE
## Acceptance Criteria
- [ ] CRITERION
EOF
)"
```
### Title Format
```
type(scope): short description
fix(mcp): validate tool handler input parameters
security(api): add rate limiting to webhook endpoint
quality(cli): replace Fatal with structured Error
test(container): add edge case tests for Stop()
docs(release): document archive format options
```
### Label Mapping
| Audit category | Labels |
|----------------|--------|
| OWASP/security | `security`, severity label, `lang:go` or `lang:php` |
| Error handling | `quality`, `complexity:medium` |
| Test coverage | `testing`, `complexity:medium` |
| Performance | `performance`, severity label |
| Code complexity | `quality`, `complexity:large` |
| Documentation | `docs`, `complexity:small` |
| Input validation | `security`, `quality` |
| Race conditions | `security`, `performance`, `complexity:large` |
### Step 4: Detect Patterns
After creating individual issues, look for patterns:
```
3+ issues with same fix type across different packages
→ Create a framework-level issue
→ Link individual issues as children
→ The framework fix obsoletes the individual fixes
```
**Example pattern:** 5 audit findings say "add error wrapping" in different packages. The real fix is a framework-level `errors.Wrap()` helper or middleware. Create:
- 1 framework issue: "feat(errors): add contextual error wrapping middleware"
- 5 child issues: each package migration (become validation that the framework fix works)
### Step 5: Create Epic (if enough issues)
If 3+ implementation issues were created from one audit, group them into an epic using the `create-epic` flow.
If fewer than 3, just label them for direct dispatch — no epic needed.
### Step 6: Mark Audit as Processed
Once all findings have implementation issues:
```bash
# Comment linking to created issues
gh issue comment AUDIT_NUMBER --repo OWNER/REPO \
--body "Implementation issues created: #A, #B, #C, #D"
# Close the audit issue
gh issue close AUDIT_NUMBER --repo OWNER/REPO --reason completed
```
The audit is done. The implementation issues carry the work forward.
---
## Staleness Check
Before processing an audit, verify findings are still relevant:
```bash
# Check if the file/line still exists
gh api repos/OWNER/REPO/contents/PATH --jq '.sha' 2>&1
```
If the file was deleted or heavily refactored, the finding may be stale. But:
- **Don't discard stale findings.** The underlying pattern may still exist elsewhere.
- **Re-scan if stale.** The audit agent may have found something that moved, not something that was fixed.
- **Only skip if the entire category was resolved** (e.g. "add tests" but test coverage is now 90%).
---
## Training Data Value
Every issue created from an audit becomes training data:
| Issue outcome | Training value |
|---------------|----------------|
| Fixed correctly | Positive example: finding → fix |
| Fixed but review caught problems | Mixed: finding valid, fix needed iteration |
| Dismissed as not applicable | Negative example: audit produced false positive |
| Led to framework change | High value: pattern detection signal |
| Nitpick that revealed bigger issue | High value: small finding → large impact |
**None of these are worthless.** Even false positives teach the model what NOT to flag. Label the outcome in the training journal so the pipeline can sort them.
### Journal Extension for Audit-Origin Issues
```jsonc
{
// ... standard journal fields ...
"origin": {
"type": "audit",
"audit_issue": 183,
"audit_category": "owasp",
"finding_severity": "medium",
"finding_scope": "package",
"pattern_detected": true,
"framework_issue": 250
}
}
```
---
## Quick Reference
```
1. Read audit issue
2. Classify each finding (severity, type, scope, complexity)
3. Create one issue per finding (consistent title/labels)
4. Detect patterns (3+ similar → framework issue)
5. Group into epic if 3+ issues (use create-epic flow)
6. Close audit issue, link to implementation issues
```
---
*Created: 2026-02-04*
*Companion to: RFC.flow-issue-epic.md, RFC.flow-create-epic.md*

View file

@ -0,0 +1,219 @@
---
name: flow-create-epic
description: Use when grouping 3+ ungrouped issues into epics with branches. Creates parent epic issues with checklists and corresponding epic branches.
---
# Flow: Create Epic
Turn a group of related issues into an epic with child issues, an epic branch, and a parent checklist — ready for the issue-epic flow to execute.
---
## When to Use
- A repo has multiple open issues that share a theme (audit, migration, feature area)
- You want to parallelise work across agents on related tasks
- You need to track progress of a multi-issue effort
## Inputs
- **Repo**: `owner/repo`
- **Theme**: What groups these issues (e.g. "security audit", "io migration", "help system")
- **Candidate issues**: Found by label, keyword, or manual selection
## Process
### Step 1: Find Candidate Issues
Search for issues that belong together. Use structural signals only — labels, title patterns, repo.
```bash
# By label
gh search issues --repo OWNER/REPO --state open --label LABEL --json number,title
# By title pattern
gh search issues --repo OWNER/REPO --state open --json number,title \
--jq '.[] | select(.title | test("PATTERN"))'
# All open issues in a repo (for small repos)
gh issue list --repo OWNER/REPO --state open --json number,title,labels
```
Group candidates by dependency order if possible:
- **Blockers first**: Interface changes, shared types, core abstractions
- **Parallel middle**: Independent migrations, per-package work
- **Cleanup last**: Deprecation removal, docs, final validation
### Step 2: Check for Existing Epics
Before creating a new epic, check if one already exists.
```bash
# Search for issues with child checklists in the repo
gh search issues --repo OWNER/REPO --state open --json number,title,body \
--jq '.[] | select(.body | test("- \\[[ x]\\] #\\d+")) | {number, title}'
```
If an epic exists for this theme, update it instead of creating a new one.
### Step 3: Order the Children
Arrange child issues into phases based on dependencies:
```
Phase 1: Blockers (must complete before Phase 2)
- Interface definitions, shared types, core changes
Phase 2: Parallel work (independent, can run simultaneously)
- Per-package migrations, per-file changes
Phase 3: Cleanup (depends on Phase 2 completion)
- Remove deprecated code, update docs, final validation
```
Within each phase, issues are independent and can be dispatched to agents in parallel.
### Step 4: Create the Epic Issue
Create a parent issue with the child checklist.
```bash
gh issue create --repo OWNER/REPO \
--title "EPIC_TITLE" \
--label "agentic,complexity:large" \
--body "$(cat <<'EOF'
## Overview
DESCRIPTION OF THE EPIC GOAL.
## Child Issues
### Phase 1: PHASE_NAME (blocking)
- [ ] #NUM - TITLE
- [ ] #NUM - TITLE
### Phase 2: PHASE_NAME (parallelisable)
- [ ] #NUM - TITLE
- [ ] #NUM - TITLE
### Phase 3: PHASE_NAME (cleanup)
- [ ] #NUM - TITLE
## Acceptance Criteria
- [ ] CRITERION_1
- [ ] CRITERION_2
EOF
)"
```
**Checklist format matters.** The issue-epic flow detects children via `- [ ] #NUM` and `- [x] #NUM` patterns. Use exactly this format.
### Step 5: Link Children to Parent
Add a `Parent: #EPIC_NUMBER` line to each child issue body, or comment it.
```bash
for CHILD in NUM1 NUM2 NUM3; do
gh issue comment $CHILD --repo OWNER/REPO --body "Parent: #EPIC_NUMBER"
done
```
### Step 6: Create the Epic Branch
Create a branch off dev (or the repo's default branch) for the epic.
```bash
# Get default branch SHA
SHA=$(gh api repos/OWNER/REPO/git/refs/heads/dev --jq '.object.sha')
# Create epic branch
gh api repos/OWNER/REPO/git/refs -X POST \
-f ref="refs/heads/epic/EPIC_NUMBER-SLUG" \
-f sha="$SHA"
```
**Naming:** `epic/<issue-number>-<short-slug>` (e.g. `epic/118-mcp-daemon`)
### Step 7: Dispatch Blockers
Add the agent label to the first unchecked child in each phase (the blocker). Add a target branch comment.
```bash
# Label the blocker
gh issue edit CHILD_NUM --repo OWNER/REPO --add-label jules
# Comment the target branch
gh issue comment CHILD_NUM --repo OWNER/REPO \
--body "Target branch: \`epic/EPIC_NUMBER-SLUG\` (epic #EPIC_NUMBER)"
```
**IMPORTANT:** Adding the agent label (e.g. `jules`) immediately dispatches work. Only label when ready. Each label costs a daily task from the agent's quota.
---
## Creating Epics from Audit Issues
Many repos have standalone audit issues (e.g. `[Audit] Security`, `[Audit] Performance`). These can be grouped into a single audit epic per repo.
### Pattern: Audit Epic
```bash
# Find all audit issues in a repo
gh issue list --repo OWNER/REPO --state open --label jules \
--json number,title --jq '.[] | select(.title | test("\\[Audit\\]|audit:"))'
```
Group by category and create an epic:
```markdown
## Child Issues
### Security
- [ ] #36 - OWASP Top 10 security review
- [ ] #37 - Input validation and sanitization
- [ ] #38 - Authentication and authorization flows
### Quality
- [ ] #41 - Code complexity and maintainability
- [ ] #42 - Test coverage and quality
- [ ] #43 - Performance bottlenecks
### Ops
- [ ] #44 - API design and consistency
- [ ] #45 - Documentation completeness
```
Audit issues are typically independent (no phase ordering needed) — all can be dispatched in parallel.
---
## Creating Epics from Feature Issues
Feature repos (e.g. `core-claude`) may have many related feature issues that form a product epic.
### Pattern: Feature Epic
Group by dependency:
1. **Foundation**: Core abstractions the features depend on
2. **Features**: Independent feature implementations
3. **Integration**: Cross-feature integration, docs, onboarding
---
## Checklist
Before dispatching an epic:
- [ ] Candidate issues identified and ordered
- [ ] No existing epic covers this theme
- [ ] Epic issue created with `- [ ] #NUM` checklist
- [ ] Children linked back to parent (`Parent: #NUM`)
- [ ] Epic branch created (`epic/<number>-<slug>`)
- [ ] Blocker issues (Phase 1 first children) labelled for dispatch
- [ ] Target branch commented on labelled issues
- [ ] Agent quota checked (don't over-dispatch)
---
*Companion to: RFC.flow-issue-epic.md*

View file

@ -0,0 +1,273 @@
---
name: flow-gather-training-data
description: Use when capturing training data from completed flows. Records structural signals (IDs, timestamps, SHAs) to JSONL journals for model training.
---
# Flow: Gather Training Data
Continuously capture PR/issue state observations for training the agentic orchestrator model.
---
## Purpose
Build a time-series dataset of:
1. **Input signals** - PR state, CI status, review counts, timing
2. **Actions taken** - what the orchestrator decided
3. **Outcomes** - did it work? how long to resolution?
This enables training a model to predict correct actions from signals alone.
---
## Infrastructure
### InfluxDB Setup
```bash
# Install (Ubuntu 24.04)
curl -sL https://repos.influxdata.com/influxdata-archive.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/influxdata-archive.gpg
echo "deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive.gpg] https://repos.influxdata.com/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/influxdata.list
sudo apt-get update && sudo apt-get install -y influxdb2 influxdb2-cli
# Start service
sudo systemctl enable influxdb --now
# Initial setup (interactive)
influx setup \
--org agentic \
--bucket training \
--username claude \
--password <password> \
--force
# Create API token for writes
influx auth create --org agentic --write-bucket training --description "training-data-capture"
```
Store the token in `~/.influx_token` (chmod 600).
### Schema (InfluxDB Line Protocol)
```
# Measurement: pr_observation
pr_observation,repo=dappcore/core,pr=315,author=jules[bot] \
merge_state="CLEAN",mergeable=true,is_draft=false,\
checks_total=8i,checks_passing=8i,checks_failing=0i,\
reviews_approved=1i,reviews_changes_requested=0i,\
threads_total=5i,threads_unresolved=0i,\
pr_age_hours=48i,last_push_hours=2i,\
conflict_attempts=0i,review_fix_attempts=0i \
1707123600000000000
# Measurement: action_taken
action_taken,repo=dappcore/core,pr=315 \
action="wait",reason="auto-merge enabled, checks passing" \
1707123600000000000
# Measurement: outcome
outcome,repo=dappcore/core,pr=315 \
result="success",detail="merged via auto-merge",resolution_hours=0.5 \
1707125400000000000
```
---
## Capture Script
Location: `~/infra/tasks-agentic/training-data/capture-to-influx.sh`
```bash
#!/bin/bash
# capture-to-influx.sh - Capture PR states to InfluxDB
set -euo pipefail
INFLUX_HOST="${INFLUX_HOST:-http://localhost:8086}"
INFLUX_ORG="${INFLUX_ORG:-agentic}"
INFLUX_BUCKET="${INFLUX_BUCKET:-training}"
INFLUX_TOKEN="${INFLUX_TOKEN:-$(cat ~/.influx_token 2>/dev/null)}"
REPO="${1:-dappcore/core}"
capture_pr_to_influx() {
local repo=$1
local pr=$2
local timestamp
timestamp=$(date +%s%N)
# Get PR data
local data
data=$(gh pr view "$pr" --repo "$repo" --json \
number,mergeable,mergeStateStatus,statusCheckRollup,\
latestReviews,reviewDecision,labels,author,createdAt,updatedAt,\
commits,autoMergeRequest,isDraft 2>/dev/null)
# Extract fields
local merge_state=$(echo "$data" | jq -r '.mergeStateStatus // "UNKNOWN"')
local mergeable=$(echo "$data" | jq -r 'if .mergeable == "MERGEABLE" then "true" else "false" end')
local is_draft=$(echo "$data" | jq -r '.isDraft // false')
local checks_total=$(echo "$data" | jq '[.statusCheckRollup[]? | select(.name != null)] | length')
local checks_passing=$(echo "$data" | jq '[.statusCheckRollup[]? | select(.conclusion == "SUCCESS")] | length')
local checks_failing=$(echo "$data" | jq '[.statusCheckRollup[]? | select(.conclusion == "FAILURE")] | length')
local reviews_approved=$(echo "$data" | jq '[.latestReviews[]? | select(.state == "APPROVED")] | length')
local reviews_changes=$(echo "$data" | jq '[.latestReviews[]? | select(.state == "CHANGES_REQUESTED")] | length')
local author=$(echo "$data" | jq -r '.author.login // "unknown"')
local auto_merge=$(echo "$data" | jq -r 'if .autoMergeRequest != null then "true" else "false" end')
# Calculate ages
local created=$(echo "$data" | jq -r '.createdAt')
local updated=$(echo "$data" | jq -r '.updatedAt')
# NOTE: date -d is GNU (Linux). On macOS use: date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s
local pr_age_hours=$(( ($(date +%s) - $(date -d "$created" +%s)) / 3600 ))
local last_activity_hours=$(( ($(date +%s) - $(date -d "$updated" +%s)) / 3600 ))
# Build line protocol
local line="pr_observation,repo=${repo//\//_},pr=${pr},author=${author} "
line+="merge_state=\"${merge_state}\","
line+="mergeable=${mergeable},"
line+="is_draft=${is_draft},"
line+="checks_total=${checks_total}i,"
line+="checks_passing=${checks_passing}i,"
line+="checks_failing=${checks_failing}i,"
line+="reviews_approved=${reviews_approved}i,"
line+="reviews_changes_requested=${reviews_changes}i,"
line+="auto_merge_enabled=${auto_merge},"
line+="pr_age_hours=${pr_age_hours}i,"
line+="last_activity_hours=${last_activity_hours}i "
line+="${timestamp}"
# Write to InfluxDB
curl -s -XPOST "${INFLUX_HOST}/api/v2/write?org=${INFLUX_ORG}&bucket=${INFLUX_BUCKET}&precision=ns" \
-H "Authorization: Token ${INFLUX_TOKEN}" \
-H "Content-Type: text/plain" \
--data-raw "$line"
echo "Captured PR #${pr}"
}
# Capture all open PRs
for pr in $(gh pr list --repo "$REPO" --state open --json number --jq '.[].number'); do
capture_pr_to_influx "$REPO" "$pr"
done
```
---
## Cron Schedule
```bash
# Add to crontab -e
# Capture every 15 minutes
*/15 * * * * /home/claude/infra/tasks-agentic/training-data/capture-to-influx.sh dappcore/core >> /home/claude/logs/training-capture.log 2>&1
# Also capture PHP repos hourly (lower priority)
0 * * * * /home/claude/infra/tasks-agentic/training-data/capture-to-influx.sh dappcore/core-php >> /home/claude/logs/training-capture.log 2>&1
0 * * * * /home/claude/infra/tasks-agentic/training-data/capture-to-influx.sh dappcore/core-mcp >> /home/claude/logs/training-capture.log 2>&1
0 * * * * /home/claude/infra/tasks-agentic/training-data/capture-to-influx.sh dappcore/core-api >> /home/claude/logs/training-capture.log 2>&1
```
---
## Recording Actions & Outcomes
### When Orchestrator Takes Action
After any orchestration action, record it:
```bash
record_action() {
local repo=$1 pr=$2 action=$3 reason=$4
local timestamp=$(date +%s%N)
local line="action_taken,repo=${repo//\//_},pr=${pr} action=\"${action}\",reason=\"${reason}\" ${timestamp}"
curl -s -XPOST "${INFLUX_HOST}/api/v2/write?org=${INFLUX_ORG}&bucket=${INFLUX_BUCKET}&precision=ns" \
-H "Authorization: Token ${INFLUX_TOKEN}" \
--data-raw "$line"
}
# Examples:
record_action "dappcore/core" 315 "wait" "auto-merge enabled, all checks passing"
record_action "dappcore/core" 307 "request_review_fix" "unresolved threads, attempt 1"
record_action "dappcore/core" 319 "resolve_conflict" "conflict_attempts >= 2, manual resolution"
```
### When PR Resolves
When a PR merges, closes, or is escalated:
```bash
record_outcome() {
local repo=$1 pr=$2 result=$3 detail=$4 resolution_hours=$5
local timestamp=$(date +%s%N)
local line="outcome,repo=${repo//\//_},pr=${pr} result=\"${result}\",detail=\"${detail}\",resolution_hours=${resolution_hours} ${timestamp}"
curl -s -XPOST "${INFLUX_HOST}/api/v2/write?org=${INFLUX_ORG}&bucket=${INFLUX_BUCKET}&precision=ns" \
-H "Authorization: Token ${INFLUX_TOKEN}" \
--data-raw "$line"
}
# Examples:
record_outcome "dappcore/core" 315 "success" "merged via auto-merge" 0.5
record_outcome "dappcore/core" 307 "success" "merged after 2 review fix requests" 4.2
record_outcome "dappcore/core" 291 "escalated" "conflict unresolvable after manual attempt" 72.0
```
---
## Query Examples
### Flux queries for analysis
```flux
// All observations for a PR over time
from(bucket: "training")
|> range(start: -7d)
|> filter(fn: (r) => r._measurement == "pr_observation")
|> filter(fn: (r) => r.pr == "315")
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
// Action success rate by type
from(bucket: "training")
|> range(start: -30d)
|> filter(fn: (r) => r._measurement == "outcome")
|> filter(fn: (r) => r._field == "result")
|> group(columns: ["action"])
|> count()
// Average resolution time by action type
from(bucket: "training")
|> range(start: -30d)
|> filter(fn: (r) => r._measurement == "outcome")
|> filter(fn: (r) => r._field == "resolution_hours")
|> group(columns: ["action"])
|> mean()
```
---
## Export for Training
```bash
# Export to JSONL for model training
influx query '
from(bucket: "training")
|> range(start: -90d)
|> filter(fn: (r) => r._measurement == "pr_observation")
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
' --raw | jq -c '.' > training-export.jsonl
```
---
## Integration with issue-epic.md
The `issue-epic` flow should call `record_action` at each decision point:
1. **Step 3 (CI Gate)** - After checking checks: `record_action $REPO $PR "wait" "CI running"`
2. **Step 5 (Fix Review)** - After sending fix request: `record_action $REPO $PR "request_review_fix" "unresolved threads"`
3. **Step 7 (Update Branch)** - After conflict request: `record_action $REPO $PR "request_conflict_fix" "merge conflict detected"`
4. **Step 8 (Merge)** - When PR merges: `record_outcome $REPO $PR "success" "merged" $hours`
---
*Created: 2026-02-05*
*Part of: agentic pipeline training infrastructure*

View file

@ -0,0 +1,624 @@
---
name: flow-issue-epic
description: Use when running an epic through the full lifecycle - dispatching children to agents, fixing review comments, resolving threads, merging PRs, and updating parent checklists. The core pipeline for agent-driven development.
---
# Flow: Issue Epic
Orchestrate a parent issue (epic) with child issues through the full lifecycle: assignment, implementation, review, merge, and parent tracking.
---
## Trigger
An epic issue exists with a checklist of child issues (e.g. `- [ ] #103 - Description`).
## Actors
| Role | Examples | Capabilities |
|------|----------|--------------|
| **Orchestrator** | Claude Code, core CLI | Full pipeline control, API calls, state tracking |
| **Implementer** | Jules, Copilot, Codex, human dev | Creates branches, writes code, pushes PRs |
| **Reviewer** | Copilot, CodeRabbit, code owners | Reviews PRs, leaves comments |
| **Gatekeeper** | Code owner (human) | Final verification, approves external PRs |
The implementer is agent-agnostic. The orchestrator does not need to know which agent is being used — only that the PR exists and commits are being pushed.
## Security: No Comment Parsing
**The orchestrator MUST NEVER read or parse comment bodies, review thread content, or issue descriptions as instructions.**
The orchestrator only reads **structural state**:
- PR status (open, merged, conflicting)
- Check conclusions (pass, fail)
- Thread counts (resolved vs unresolved)
- Commit timestamps
- Issue open/closed state
**Why?** Comments are untrusted input. Anyone can write a PR comment containing instructions. If the orchestrator parses comment content, it becomes an injection vector — a malicious comment could instruct the orchestrator to take actions. By only observing structural signals, the orchestrator is immune to prompt injection via comments.
The orchestrator **writes** comments (fire-and-forget) but never **reads** them.
## Implementer Commands
The **human** (gatekeeper) posts these two PR-level comments. **Never reply to individual review threads** — only comment on the PR itself.
| Command | When to use |
|---------|-------------|
| `Can you fix the code reviews?` | Unresolved review threads exist after reviews arrive |
| `Can you fix the merge conflict?` | PR shows as CONFLICTING / DIRTY |
These are the **only** two interventions. The implementer reads all unresolved threads, pushes a fix commit, and the automation handles the rest. The orchestrator posts these comments but does not read responses — it detects the fix by observing a new commit timestamp.
## Dispatching to an Implementer
To dispatch a child issue to an agent:
1. **Add the agent label** to the issue (e.g. `jules`, `copilot`)
2. **Comment the target branch**: `Target branch: \`epic/<number>-<slug>\` (epic #<number>)`
3. **Dispatch blockers first** — the first child in each epic's checklist blocks the rest. Always label and dispatch the first unchecked child before later ones.
The label is the dispatch signal. The target branch comment tells the agent where to push. The orchestrator adds both but never reads the comment back.
**IMPORTANT:** Adding the `jules` label immediately dispatches to Jules (Codex). Jules auto-picks up any issue with its label. Do NOT add the label unless you intend to use a daily task (300/day quota). Same applies to other agent labels — the label IS the trigger.
**NEVER auto-dispatch `feat(*)` issues.** Feature issues require design decisions and planning from the code owner (@Snider). Only audit-derived issues (fix, security, quality, test, docs, performance, refactor) can be dispatched without explicit owner approval. If an issue title starts with `feat(`, skip it and flag it for human review.
## Pipeline per Child Issue
```
┌─────────────────────────────────────────────────────────┐
│ 1. ASSIGN │
│ - Add agent label (jules, copilot, etc.) │
│ - Comment target branch on the issue │
│ - Dispatch blockers first (first unchecked child) │
│ │
│ 2. IMPLEMENT │
│ - Implementer creates branch from dev │
│ - Writes code, pushes commits │
│ - Opens PR targeting dev │
│ - Auto-merge enabled (if org member) │
│ │
│ 3. CI GATE │
│ - CI runs: build, qa, tests │
│ - If fail: implementer fixes, pushes again │
│ - Loop until green │
│ │
│ 4. REVIEW │
│ - Copilot code review (auto on push) │
│ - CodeRabbit review (auto or triggered) │
│ - Code owner review (auto-requested via CODEOWNERS) │
│ │
│ 5. FIX REVIEW COMMENTS │
│ - Comment on PR: "Can you fix the code reviews?" │
│ - Implementer reads threads, pushes fix commit │
│ - Stale reviews dismissed on push (ruleset) │
│ - New review cycle triggers on new commit │
│ - Loop steps 4-5 until reviews are clean │
│ │
│ 6. RESOLVE THREADS │
│ - Wait for new commit after "fix the code reviews" │
│ - Once commit lands: resolve ALL threads that exist │
│ before that commit timestamp │
│ - Trust the process — don't verify individual fixes │
│ - Required by ruleset before merge │
│ │
│ 7. UPDATE BRANCH │
│ - If behind dev: update via API or comment │
│ - If conflicting: "Can you fix the merge conflict?" │
│ - If CI fails after update: implementer auto-fixes │
│ │
│ 8. MERGE │
│ - All checks green + threads resolved + up to date │
│ - Merge queue picks up PR (1 min wait, ALLGREEN) │
│ - Squash merge into dev │
│ │
│ 9. UPDATE PARENT │
│ - Tick checkbox on parent issue │
│ - Close child issue if not auto-closed │
│ │
│ 10. CAPTURE TRAINING DATA │
│ - Write journal entry (JSONL) for completed flow │
│ - Record: IDs, SHAs, timestamps, cycle counts │
│ - Record: instructions sent, automations performed │
│ - NO content (no comments, no messages, no bodies) │
│ - Structural signals only — safe for training │
└─────────────────────────────────────────────────────────┘
```
## Observed Response Times
Implementer agents respond to PR comments with a fix commit. The delay between instruction and commit is the **response time**. This is a key metric for training data.
| Signal | Observed timing | Notes |
|--------|-----------------|-------|
| 👀 emoji reaction on comment | Seconds (Jules/Gemini) | Acknowledgment — Jules has seen and picked up the instruction |
| `fix the merge conflict` commit | ~3m 42s (Jules/Gemini) | Comment → commit delta |
| `fix the code reviews` commit | ~5-15m (Jules/Gemini) | Varies with thread count |
### Acknowledgment Signal
Jules adds an 👀 (eyes) emoji reaction to PR comments almost immediately when it picks up a task. This is a **structural signal** (reaction type, not content) that confirms the agent has seen the instruction. The orchestrator can check for this reaction via the API:
```bash
# Check if Jules reacted to a comment (structural — reaction type only)
gh api repos/OWNER/REPO/issues/comments/COMMENT_ID/reactions \
--jq '.[] | select(.content == "eyes") | {user: .user.login, created_at: .created_at}'
```
**Timeline:** 👀 reaction (seconds) → fix commit (~3-15 min) → structural state change. If no 👀 reaction within ~30 seconds, the agent may not have picked up the instruction — check if the issue still has the agent label.
**Important:** A response commit does not guarantee the issue is fixed. When multiple PRs merge into dev in rapid succession, each merge changes the target branch — creating **new, different conflicts** on the remaining PRs even after the agent resolved the previous one. This is a cascade effect of parallel work on overlapping files. The orchestrator must re-check structural state after each response and re-send the instruction if the blocker persists. This creates a loop:
```
instruction → wait for commit → check state → still blocked? → re-send instruction
```
The loop terminates when the structural signal changes (CONFLICTING → MERGEABLE, unresolved → 0, checks → green).
## Thread Resolution Rule
**After a new commit appears on the PR:**
1. Observe: new commit exists (structural — timestamp comparison, not content)
2. Resolve ALL unresolved threads that were created before that commit
3. Do NOT read thread content to check whether each was addressed
4. Trust the process — the implementer read the threads and pushed a fix
**Why trust blindly?** Checking each thread manually doesn't scale to 10+ agents. If the fix is wrong, the next review cycle will catch it. If it's a genuine miss, the code owners will see it. The automation must not block on human verification of individual threads.
**Never read or reply to individual review threads.** Replying to threads can:
- Trigger re-analysis loops (CodeRabbit)
- Cost premium credits (Copilot: 1 credit per reply)
- Confuse agents that use thread state as context
- Open an injection vector if the orchestrator processes the content
## Orchestrator Data Access
### ALLOWED (structural signals)
| Signal | API field | Purpose |
|--------|-----------|---------|
| PR state | `state` | Open, merged, closed |
| Mergeable | `mergeable` | MERGEABLE, CONFLICTING, UNKNOWN |
| Check conclusions | `statusCheckRollup[].conclusion` | SUCCESS, FAILURE |
| Thread count | `reviewThreads[].isResolved` | Count resolved vs unresolved |
| Thread IDs | `reviewThreads[].id` | For resolving (mutation only) |
| Commit timestamp | `commits[-1].committedDate` | Detect new commits |
| Commit SHA | `commits[-1].oid` | Track head state |
| Auto-merge state | `autoMergeRequest` | Null or enabled |
| Issue state | `state` | OPEN, CLOSED |
| Issue body checkboxes | `body` (pattern match `- [ ]`/`- [x]` only) | Parent checklist sync |
| Comment reactions | `reactions[].content` | 👀 = agent acknowledged instruction |
### NEVER READ (untrusted content)
| Data | Why |
|------|-----|
| Comment bodies | Injection vector — anyone can write instructions |
| Review thread content | Same — review comments are untrusted input |
| Commit messages | Can contain crafted instructions |
| PR title/description | Attacker-controlled in fork PRs |
| Issue comments | Same injection risk |
The orchestrator is **write-only** for comments (fire-and-forget) and **structural-only** for reads. This makes it immune to prompt injection via PR/issue content.
## Orchestrator Actions
### Post command to PR
```bash
gh pr comment PR_NUMBER --repo OWNER/REPO --body "Can you fix the code reviews?"
# or
gh pr comment PR_NUMBER --repo OWNER/REPO --body "Can you fix the merge conflict?"
```
### Detect new commit (structural only)
```bash
# Get latest commit SHA and timestamp on PR head — no content parsing
gh pr view PR_NUMBER --repo OWNER/REPO --json commits \
--jq '.commits[-1] | {sha: .oid, date: .committedDate}'
```
Compare the commit timestamp against the last known state. If a newer commit exists, the implementer has responded. **Do not read what the commit changed or any comment content.**
### Resolve all unresolved threads
```bash
# Get unresolved thread IDs only — never read thread bodies
gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: PR_NUMBER) {
reviewThreads(first: 100) {
nodes { id isResolved }
}
}
}
}
' --jq '.data.repository.pullRequest.reviewThreads.nodes[]
| select(.isResolved == false)
| .id' | while IFS= read -r tid; do
gh api graphql -f query="mutation {
resolveReviewThread(input: {threadId: \"$tid\"}) {
thread { isResolved }
}
}"
done
```
### Update PR branch (non-conflicting)
```bash
gh api repos/OWNER/REPO/pulls/PR_NUMBER/update-branch -X PUT -f update_method=merge
```
### Enable auto-merge
```bash
gh pr merge PR_NUMBER --repo OWNER/REPO --auto --squash
```
### Update parent issue checklist
```bash
BODY=$(gh issue view PARENT_NUMBER --repo OWNER/REPO --json body --jq '.body')
UPDATED=$(echo "$BODY" | sed "s/- \[ \] #CHILD_NUMBER/- [x] #CHILD_NUMBER/")
gh issue edit PARENT_NUMBER --repo OWNER/REPO --body "$UPDATED"
```
### Close child issue
```bash
gh issue close CHILD_NUMBER --repo OWNER/REPO --reason completed
```
## Unsticking a PR — Full Sequence
When a PR is stuck (blocked, not merging), run these steps in order:
```
1. Has unresolved review threads?
YES → Comment "Can you fix the code reviews?"
Wait for new commit from implementer
2. New commit landed?
YES → Resolve all threads before that commit timestamp
3. Is PR conflicting?
YES → Comment "Can you fix the merge conflict?"
Wait for force-push or merge commit from implementer
4. Is PR behind dev but not conflicting?
YES → Update branch via API
5. Is auto-merge enabled?
NO → Enable auto-merge (squash)
6. Are all checks green?
NO → Wait. Implementer auto-fixes CI failures.
YES → Merge queue picks it up. Done.
```
## Parallelisation Rules
1. **Child issues within a phase are independent** — can run 10+ simultaneously
2. **Cross-phase dependencies** — Phase 2 can't start until Phase 1 is done
3. **Thread resolution** — wait for implementer's fix commit, then resolve all pre-commit threads
4. **Merge queue serialises merges** — ALLGREEN strategy, no conflict pile-up with 1 min wait
5. **Parent checklist updates are atomic** — read-modify-write, risk of race with parallel merges
### Race Condition: Parent Checklist
When multiple child PRs merge simultaneously, concurrent `gh issue edit` calls can overwrite each other. Mitigations:
1. **Optimistic retry**: Read body, modify, write. If body changed between read and write, retry.
2. **Queue updates**: Collect merged children, batch-update parent once per minute.
3. **Use sub-issues API**: If available, GitHub tracks state automatically (see `sub_issue_write` MCP tool).
## Scaling to 10+ Developers
| Concern | Solution |
|---------|----------|
| Review bottleneck | Auto-reviews (Copilot, CodeRabbit) + CODEOWNERS auto-request |
| Thread resolution | Orchestrator resolves after fix commit (trust the process) |
| Parent tracking | Orchestrator updates checklist on merge events |
| Merge conflicts | Comment "fix the merge conflict", agent handles it |
| Agent cost | Free agents first (CodeRabbit, Gemini), paid last (Copilot credits) |
| Attribution | Each PR linked to child issue, child linked to parent |
| Stale reviews | Ruleset dismisses on push, forces re-review |
| Agent variety | Commands are agent-agnostic — works with any implementer |
## Automation Targets
### Currently Automated
- PR auto-merge for org members
- CI (build + QA with fix hints)
- Copilot code review on push
- Code owner review requests (CODEOWNERS)
- Merge queue with ALLGREEN
- Stale review dismissal on push
### Needs Automation (next)
- [ ] Detect when reviews arrive → auto-comment "fix the code reviews"
- [ ] Detect fix commit → auto-resolve pre-commit threads
- [ ] Detect merge conflict → auto-comment "fix the merge conflict"
- [ ] On merge event → tick parent checklist + close child issue
- [ ] State snapshot: periodic capture of epic progress
- [ ] Webhook/polling: trigger orchestrator on PR state changes
### `core dev epic` Command
```bash
core dev epic 101 # Show epic state (like state snapshot)
core dev epic 101 --sync # Update parent checklist from closed children
core dev epic 101 --dispatch # Assign unstarted children to available agents
core dev epic 101 --resolve PR_NUM # Resolve all threads on a PR after fix commit
core dev epic 101 --unstick # Run unstick sequence on all blocked PRs
core dev epic 101 --watch # Watch for events, auto-handle everything
```
## Stage 10: Training Data Capture
Every completed child issue flow produces a **journal entry** — a structured record of the full lifecycle that can be reconstructed as timeseries data for model training.
### Journal Schema
Each completed flow writes one JSONL record:
```jsonc
{
// Identity
"epic_number": 101,
"child_number": 111,
"pr_number": 288,
"repo": "dappcore/core",
// Timestamps (for timeseries reconstruction)
"issue_created_at": "2026-02-03T10:00:00Z",
"pr_opened_at": "2026-02-04T12:00:00Z",
"first_ci_pass_at": "2026-02-04T12:15:00Z",
"merged_at": "2026-02-04T15:33:10Z",
// Commits (ordered, SHAs only — no messages)
"commits": [
{"sha": "abc1234", "timestamp": "2026-02-04T12:00:00Z"},
{"sha": "def5678", "timestamp": "2026-02-04T14:20:00Z"}
],
// Review cycles (structural only — no content)
"review_cycles": [
{
"cycle": 1,
"thread_ids": ["PRRT_kwDO...", "PRRT_kwDO..."],
"thread_count": 3,
"instruction_sent": "fix_code_reviews",
"instruction_at": "2026-02-04T13:00:00Z",
"response_commit_sha": "def5678",
"response_commit_at": "2026-02-04T14:20:00Z",
"threads_resolved_at": "2026-02-04T14:25:00Z"
}
],
// Merge conflict cycles (if any)
"conflict_cycles": [
{
"cycle": 1,
"instruction_sent": "fix_merge_conflict",
"instruction_at": "2026-02-04T14:30:00Z",
"response_commit_sha": "ghi9012",
"response_commit_at": "2026-02-04T14:45:00Z"
}
],
// CI runs (structural — pass/fail only, no log content)
"ci_runs": [
{"sha": "abc1234", "conclusion": "failure", "checks_failed": ["qa"]},
{"sha": "def5678", "conclusion": "success", "checks_failed": []}
],
// Automations performed by orchestrator
"automations": [
{"action": "enable_auto_merge", "at": "2026-02-04T12:01:00Z"},
{"action": "resolve_threads", "count": 3, "at": "2026-02-04T14:25:00Z"},
{"action": "update_branch", "at": "2026-02-04T14:26:00Z"},
{"action": "tick_parent_checklist", "child": 111, "at": "2026-02-04T15:34:00Z"}
],
// Outcome
"outcome": "merged",
"total_review_cycles": 1,
"total_conflict_cycles": 0,
"total_ci_runs": 2,
"duration_seconds": 12790
}
```
### What We Capture
| Field | Source | Content? |
|-------|--------|----------|
| Issue/PR numbers | GitHub API | IDs only |
| Commit SHAs + timestamps | `commits[].oid`, `committedDate` | No messages |
| Review thread IDs | `reviewThreads[].id` | No bodies |
| Thread counts | `length` of filtered nodes | Numeric only |
| Instructions sent | Fixed enum: `fix_code_reviews`, `fix_merge_conflict` | No free text |
| CI conclusions | `statusCheckRollup[].conclusion` | Pass/fail only |
| Automation actions | Orchestrator's own log | Known action types |
**No untrusted content is captured.** Thread bodies, commit messages, PR descriptions, and comment text are excluded. The journal is safe to use for training without injection risk from the data itself.
### Storage
```
.core/training/
├── journals/
│ ├── epic-101-child-102.jsonl
│ ├── epic-101-child-107.jsonl
│ ├── epic-101-child-111.jsonl
│ └── ...
└── index.jsonl # One line per completed flow, for quick queries
```
### Training Pipeline
```
1. CAPTURE
Orchestrator writes journal on merge → .core/training/journals/
2. REVIEW (human)
- Spot-check journals for anomalies
- Flag flows where agents missed reviews or introduced regressions
- Identify patterns: which check types fail most, how many cycles per fix
- Check for injection attempts (thread IDs referencing unexpected data)
3. CLEAN
- Remove incomplete flows (PR closed without merge)
- Normalise timestamps to relative offsets (t+0, t+30s, t+120s)
- Strip org-specific IDs if publishing externally
- Validate schema conformance
4. TRANSFORM
- Convert to training format (instruction/response pairs):
Input: {structural state before action}
Output: {action taken by orchestrator}
- Generate negative examples from failed flows
- Aggregate cycle counts into difficulty scores per issue type
5. TRAIN
- Fine-tune model for IDE integration (JetBrains plugin via Core MCP)
- Model learns: given PR state → what action to take next
- Developers get in-IDE suggestions: "This PR has 3 unresolved threads,
run 'fix the code reviews'?"
6. EVALUATE
- Compare model suggestions against actual orchestrator actions
- Track precision/recall on action prediction
- Retrain on new journals as they accumulate
```
### `core dev training` Command
```bash
core dev training capture PR_NUM # Write journal for a completed PR
core dev training index # Rebuild index from journals
core dev training validate # Schema-check all journals
core dev training export --clean # Export cleaned dataset for training
core dev training stats # Summary: flows, avg cycles, common failures
```
## Epic Branches
When multiple epics run in the same repo, child PRs target an **epic branch** instead of dev. This isolates parallel work and avoids cascade conflicts.
```
dev
├── epic/118-mcp-daemon ← children #119-126 target here
├── epic/127-unify-log ← children #128-132 target here
└── epic/133-help-system ← children #134-139 target here
```
**Branch lifecycle:**
1. Create `epic/<number>-<slug>` from dev HEAD
2. Child PRs target the epic branch (not dev)
3. Children merge into epic branch — no cross-epic conflicts
4. When epic is complete: merge epic branch → dev (resolve conflicts once)
5. Delete epic branch
**Naming:** `epic/<issue-number>-<short-slug>`
## Model Benchmarking
The epic flow is agent-agnostic by design. This makes it a natural benchmarking harness — give the same issue to different models and compare the results.
### How It Works
1. **Same issue, different implementers.** Reopen a closed child issue (or create duplicates) and assign to a different model. The issue spec, acceptance criteria, and CI checks are identical — only the implementer changes.
2. **Epic branches isolate the work.** Each model's attempt lives in its own PR against the epic branch. No interference between attempts.
3. **Journal data captures everything.** The training data journal records which model was the implementer, how many review cycles it took, how many CI failures, response times, and whether it merged. All structural — no content parsing.
### Journal Schema Extension
Add `implementer` to the journal record:
```jsonc
{
// ... existing fields ...
// Model identification (structural — from PR author, not content)
"implementer": {
"login": "google-labs-jules[bot]", // from PR author
"model": "gemini", // mapped from known bot logins
"provider": "google"
}
}
```
Known bot login → model mapping:
| Login | Model | Provider |
|-------|-------|----------|
| `google-labs-jules[bot]` | Gemini | Google |
| `app/copilot-swe-agent` | Copilot | GitHub/OpenAI |
| `claude-code` | Claude | Anthropic |
| *(human login)* | human | — |
### What We Compare
All metrics come from structural signals — no subjective quality judgements during the flow.
| Metric | Source | Lower is better? |
|--------|--------|-------------------|
| Total review cycles | Journal `total_review_cycles` | Yes |
| Total CI failures | Journal `total_ci_runs` where conclusion=failure | Yes |
| Conflict cycles | Journal `total_conflict_cycles` | Yes |
| Response time (instruction → commit) | Timestamp delta | Yes |
| Time to merge (PR open → merged) | Timestamp delta | Yes |
| Lines changed | PR `additions + deletions` (structural) | Neutral |
### Comparison Modes
**A/B on same issue:** Reopen an issue, assign to model B, compare journals.
**Parallel on different issues:** Run model A on epic #118, model B on epic #133. Compare aggregate metrics across similar-complexity issues.
**Round-robin:** For a large epic, alternate child issues between models. Compare per-child metrics within the same epic.
### Post-Flow Quality Review
The structural metrics tell you speed and iteration count, but not code quality. After both models complete, a **human or reviewer agent** can compare:
- Did the code actually solve the issue?
- Is the approach idiomatic for the codebase?
- Were review comments substantive or noise?
- Did the model introduce regressions?
This review happens **outside the flow** — it's a separate step that feeds back into the training pipeline. The orchestrator never makes quality judgements; it only observes structural state.
### Budget Management
| Provider | Quota | Reset |
|----------|-------|-------|
| Gemini (Jules) | 300 tasks/day | Daily |
| Google Ultra | Separate quota | Weekly |
| Copilot | 100 premium requests/month | Monthly |
| Claude (API) | Pay-per-token | — |
**Strategy:** Burn free/included quotas first (Jules, Copilot), use paid models (Claude API) for complex issues or final verification. Track spend per model in journal metadata.
### `core dev benchmark` Command
```bash
core dev benchmark 118 --models gemini,claude # Compare models on epic #118
core dev benchmark report # Aggregate comparison report
core dev benchmark leaderboard # Per-model stats across all epics
```
---
*Created: 2026-02-04*
*Updated: 2026-02-04 — added epic branches, model benchmarking, budget tracking*
*Context: Epics #101, #118, #127, #133 active. 290 Jules tasks remaining.*

View file

@ -0,0 +1,663 @@
---
name: flow-issue-orchestrator
description: Use when onboarding a repo into the agentic pipeline. End-to-end flow covering audit → epic → execute for a complete repository transformation.
---
# Flow: Issue Orchestrator
End-to-end pipeline that takes a repo from raw audit findings to running epics with agents. Sequences three flows: **audit-issues****create-epic****issue-epic**.
---
## When to Use
- Onboarding a new repo into the agentic pipeline
- Processing accumulated audit issues across the org
- Bootstrapping epics for repos that have open issues but no structure
## Pipeline Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ │
│ STAGE 1: AUDIT flow: audit-issues │
│ ─────────────── │
│ Input: Repo with [Audit] issues │
│ Output: Implementation issues (1 per finding) │
│ │
│ - Read each audit issue │
│ - Classify findings (severity, type, scope, complexity) │
│ - Create one issue per finding │
│ - Detect patterns (3+ similar → framework issue) │
│ - Close audit issues, link to children │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ STAGE 2: ORGANISE flow: create-epic │
│ ───────────────── │
│ Input: Repo with implementation issues (from Stage 1) │
│ Output: Epic issues with children, branches, phase ordering │
│ │
│ - Group issues by theme (security, quality, testing, etc.) │
│ - Order into phases (blockers → parallel → cleanup) │
│ - Create epic parent issue with checklist │
│ - Link children to parent │
│ - Create epic branch off default branch │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ STAGE 3: EXECUTE flow: issue-epic │
│ ──────────────── │
│ Input: Epic with children, branch, phase ordering │
│ Output: Merged PRs, closed issues, training data │
│ │
│ - Dispatch Phase 1 blockers to agents (add label) │
│ - Monitor: CI, reviews, conflicts, merges │
│ - Intervene: "fix code reviews" / "fix merge conflict" │
│ - Resolve threads, update branches, tick parent checklist │
│ - When phase complete → dispatch next phase │
│ - When epic complete → merge epic branch to dev │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## Running the Pipeline
### Prerequisites
- `gh` CLI authenticated with org access
- Agent label exists in the repo (e.g. `jules`)
- Repo has CI configured (or agent handles it)
- CODEOWNERS configured for auto-review requests
### Stage 1: Audit → Implementation Issues
For each repo with `[Audit]` issues:
```bash
# 1. List audit issues
gh issue list --repo dappcore/REPO --state open \
--json number,title --jq '.[] | select(.title | test("\\[Audit\\]|audit:"))'
# 2. For each audit issue, run the audit-issues flow:
# - Read the audit body
# - Classify each finding
# - Create implementation issues
# - Detect patterns → create framework issues
# - Close audit, link to children
# 3. Verify: count new issues created
gh issue list --repo dappcore/REPO --state open --label audit \
--json number --jq 'length'
```
**Agent execution:** This stage can be delegated to a subagent with the audit-issues flow as instructions. The subagent reads audit content (allowed — it's creating issues, not orchestrating PRs) and creates structured issues.
```bash
# Example: task a subagent to process all audits in a repo
# Prompt: "Run RFC.flow-audit-issues.md on dappcore/REPO.
# Process all [Audit] issues. Create implementation issues.
# Detect patterns. Create framework issues if 3+ similar."
```
### Stage 2: Group into Epics
After Stage 1 produces implementation issues:
```bash
# 1. List all open issues (implementation issues from Stage 1 + any pre-existing)
gh issue list --repo dappcore/REPO --state open \
--json number,title,labels --jq 'sort_by(.number) | .[]'
# 2. Check for existing epics
gh search issues --repo dappcore/REPO --state open --json number,title,body \
--jq '.[] | select(.body | test("- \\[[ x]\\] #\\d+")) | {number, title}'
# 3. Group issues by theme, create epics per create-epic flow:
# - Create epic parent issue with checklist
# - Link children to parent (comment "Parent: #EPIC")
# - Create epic branch: epic/<number>-<slug>
# 4. Verify: epic exists with children
gh issue view EPIC_NUMBER --repo dappcore/REPO
```
**Grouping heuristics:**
| Signal | Grouping |
|--------|----------|
| Same `audit` label + security theme | → Security epic |
| Same `audit` label + quality theme | → Quality epic |
| Same `audit` label + testing theme | → Testing epic |
| Same `audit` label + docs theme | → Documentation epic |
| All audit in small repo (< 5 issues) | Single audit epic |
| Feature issues sharing a subsystem | → Feature epic |
**Small repos (< 5 audit issues):** Create one epic per repo covering all audit findings. No need to split by theme.
**Large repos (10+ audit issues):** Split into themed epics (security, quality, testing, docs). Each epic should have 3-10 children.
### Stage 3: Dispatch and Execute
After Stage 2 creates epics:
```bash
# 1. For each epic, dispatch Phase 1 blockers:
gh issue edit CHILD_NUM --repo dappcore/REPO --add-label jules
gh issue comment CHILD_NUM --repo dappcore/REPO \
--body "Target branch: \`epic/EPIC_NUMBER-SLUG\` (epic #EPIC_NUMBER)"
# 2. Monitor and intervene per issue-epic flow
# 3. When Phase 1 complete → dispatch Phase 2
# 4. When all phases complete → merge epic branch to dev
```
**IMPORTANT:** Adding the `jules` label costs 1 daily task (300/day). Calculate total dispatch cost before starting:
```bash
# Count total children across all epics about to be dispatched
TOTAL=0
for EPIC in NUM1 NUM2 NUM3; do
COUNT=$(gh issue view $EPIC --repo dappcore/REPO --json body --jq \
'[.body | split("\n")[] | select(test("^- \\[ \\] #"))] | length')
TOTAL=$((TOTAL + COUNT))
echo "Epic #$EPIC: $COUNT children"
done
echo "Total dispatch cost: $TOTAL tasks"
```
---
## Repo Inventory
Current state of repos needing orchestration (as of 2026-02-04):
| Repo | Open | Audit | Epics | Default Branch | Stage |
|------|------|-------|-------|----------------|-------|
| `core` | 40+ | 0 | 8 (#101,#118,#127,#133,#299-#302) | `dev` | Stage 3 (executing) |
| `core-php` | 28 | 15 | 0 | `dev` | **Stage 1 ready** |
| `core-claude` | 30 | 0 | 0 | `dev` | Stage 2 (features, no audits) |
| `core-api` | 22 | 3 | 0 | `dev` | **Stage 1 ready** |
| `core-admin` | 14 | 2 | 0 | `dev` | **Stage 1 ready** |
| `core-mcp` | 24 | 5 | 0 | `dev` | **Stage 1 ready** |
| `core-tenant` | 14 | 2 | 0 | `dev` | **Stage 1 ready** |
| `core-developer` | 19 | 2 | 0 | `dev` | **Stage 1 ready** |
| `core-service-commerce` | 30 | 2 | 0 | `dev` | **Stage 1 ready** |
| `core-devops` | 3 | 1 | 0 | `dev` | **Stage 1 ready** |
| `core-agent` | 14 | 0 | 0 | `dev` | Stage 2 (features, no audits) |
| `core-template` | 12 | 1 | 0 | `dev` | **Stage 1 ready** |
| `build` | 9 | 1 | 0 | `dev` | **Stage 1 ready** |
| `ansible-coolify` | 1 | 1 | 0 | `main` | **Stage 1 ready** |
| `docker-server-php` | 1 | 1 | 0 | `main` | **Stage 1 ready** |
| `docker-server-blockchain` | 1 | 1 | 0 | `main` | **Stage 1 ready** |
### Priority Order
Process repos in this order (most issues = most value from epic structure):
```
Tier 1 — High issue count, audit-ready:
1. core-php (28 open, 15 audit → 1-2 audit epics)
2. core-mcp (24 open, 5 audit → 1 audit epic)
3. core-api (22 open, 3 audit → 1 audit epic)
Tier 2 — Medium issue count:
4. core-developer (19 open, 2 audit → 1 small epic)
5. core-admin (14 open, 2 audit → 1 small epic)
6. core-tenant (14 open, 2 audit → 1 small epic)
Tier 3 — Feature repos (no audits, skip Stage 1):
7. core-claude (30 open, 0 audit → feature epics via Stage 2)
8. core-agent (14 open, 0 audit → feature epics via Stage 2)
Tier 4 — Small repos (1-2 audit issues, single epic each):
9. core-service-commerce (30 open, 2 audit)
10. core-template (12 open, 1 audit)
11. build (9 open, 1 audit)
12. core-devops (3 open, 1 audit)
13. ansible-coolify (1 open, 1 audit)
14. docker-server-php (1 open, 1 audit)
15. docker-server-blockchain (1 open, 1 audit)
```
---
## Full Repo Onboarding Sequence
Step-by-step for onboarding a single repo:
```bash
REPO="dappcore/REPO_NAME"
ORG="dappcore"
# ─── STAGE 1: Process Audits ───
# List audit issues
AUDITS=$(gh issue list --repo $REPO --state open \
--json number,title --jq '.[] | select(.title | test("\\[Audit\\]|audit:")) | .number')
# For each audit, create implementation issues (run audit-issues flow)
for AUDIT in $AUDITS; do
echo "Processing audit #$AUDIT..."
# Subagent or manual: read audit, classify, create issues
# See RFC.flow-audit-issues.md for full process
done
# Verify implementation issues created
gh issue list --repo $REPO --state open --json number,title,labels \
--jq '.[] | "\(.number)\t\(.title)"'
# ─── STAGE 2: Create Epics ───
# List all open issues for grouping
gh issue list --repo $REPO --state open --json number,title,labels \
--jq 'sort_by(.number) | .[] | "\(.number)\t\(.title)\t\(.labels | map(.name) | join(","))"'
# Group by theme, create epic(s) per create-epic flow
# For small repos: 1 epic covering everything
# For large repos: split by security/quality/testing/docs
# Get default branch SHA
DEFAULT_BRANCH="dev" # or "main" for infra repos
SHA=$(gh api repos/$REPO/git/refs/heads/$DEFAULT_BRANCH --jq '.object.sha')
# Create epic issue (fill in children from grouping)
EPIC_URL=$(gh issue create --repo $REPO \
--title "epic(audit): Audit findings implementation" \
--label "agentic,complexity:large" \
--body "BODY_WITH_CHILDREN")
EPIC_NUMBER=$(echo $EPIC_URL | grep -o '[0-9]*$')
# Link children
for CHILD in CHILD_NUMBERS; do
gh issue comment $CHILD --repo $REPO --body "Parent: #$EPIC_NUMBER"
done
# Create epic branch
gh api repos/$REPO/git/refs -X POST \
-f ref="refs/heads/epic/$EPIC_NUMBER-audit" \
-f sha="$SHA"
# ─── STAGE 3: Dispatch ───
# Label Phase 1 blockers for agent dispatch
for BLOCKER in PHASE1_NUMBERS; do
gh issue edit $BLOCKER --repo $REPO --add-label jules
gh issue comment $BLOCKER --repo $REPO \
--body "Target branch: \`epic/$EPIC_NUMBER-audit\` (epic #$EPIC_NUMBER)"
done
# Monitor via issue-epic flow
echo "Epic #$EPIC_NUMBER dispatched. Monitor via issue-epic flow."
```
---
## Parallel Repo Processing
Multiple repos can be processed simultaneously since they're independent. The constraint is agent quota, not repo count.
### Budget Planning
```
Daily Jules quota: 300 tasks
Tasks used today: N
Available for dispatch:
Tier 1 repos: ~15 + 5 + 3 = 23 audit issues → ~50 implementation issues
Tier 2 repos: ~2 + 2 + 2 = 6 audit issues → ~15 implementation issues
Tier 4 repos: ~8 audit issues → ~20 implementation issues
Total potential children: ~85
Dispatch all Phase 1 blockers: ~15-20 tasks (1 per epic)
Full dispatch all children: ~85 tasks
```
### Parallel Stage 1 (safe — no agent cost)
Stage 1 (audit processing) is free — it creates issues, doesn't dispatch agents. Run all repos in parallel:
```bash
# Subagent per repo — all can run simultaneously
for REPO in core-php core-mcp core-api core-admin core-tenant \
core-developer core-service-commerce core-devops \
core-template build ansible-coolify \
docker-server-php docker-server-blockchain; do
echo "Subagent: run audit-issues on dappcore/$REPO"
done
```
### Parallel Stage 2 (safe — no agent cost)
Stage 2 (epic creation) is also free. Run after Stage 1 completes per repo.
### Controlled Stage 3 (costs agent quota)
Stage 3 dispatch is where budget matters. Options:
| Strategy | Tasks/day | Throughput | Risk |
|----------|-----------|------------|------|
| Conservative | 10-20 | 2-3 repos | Low — room for retries |
| Moderate | 50-80 | 5-8 repos | Medium — watch for cascade conflicts |
| Aggressive | 150-200 | 10+ repos | High — little room for iteration |
**Recommended:** Start conservative. Dispatch 1 epic per Tier 1 repo (3 epics, ~10 Phase 1 blockers). Monitor for a day. If agents handle well, increase.
---
## Testing the Pipeline
### Test Plan: Onboard Tier 1 Repos
Run the full pipeline on `core-php`, `core-mcp`, and `core-api` to validate the process before scaling to all repos.
#### Step 1: Audit Processing (Stage 1)
```bash
# Process each repo's audit issues — can run in parallel
# These are subagent tasks, each gets the audit-issues flow as instructions
# core-php: 15 audit issues (largest, best test case)
# Prompt: "Run RFC.flow-audit-issues.md on dappcore/core-php"
# core-mcp: 5 audit issues
# Prompt: "Run RFC.flow-audit-issues.md on dappcore/core-mcp"
# core-api: 3 audit issues
# Prompt: "Run RFC.flow-audit-issues.md on dappcore/core-api"
```
#### Step 2: Epic Creation (Stage 2)
After Stage 1, group issues into epics:
```bash
# core-php: 15 audit issues → likely 2-3 themed epics
# Security epic, Quality epic, possibly Testing epic
# core-mcp: 5 audit issues → 1 audit epic
# All findings in single epic
# core-api: 3 audit issues → 1 audit epic
# All findings in single epic
```
#### Step 3: Dispatch (Stage 3)
```bash
# Start with 1 blocker per epic to test the flow
# core-php epic(s): 2-3 blockers dispatched
# core-mcp epic: 1 blocker dispatched
# core-api epic: 1 blocker dispatched
# Total: ~5 tasks from Jules quota
```
#### Step 4: Validate
After first round of PRs arrive:
- [ ] PRs target correct epic branches
- [ ] CI runs and agent fixes failures
- [ ] Reviews arrive (Copilot, CodeRabbit)
- [ ] "Fix code reviews" produces fix commit
- [ ] Thread resolution works
- [ ] Auto-merge completes
- [ ] Parent checklist updated
### Test Plan: PHP Repos (Laravel)
PHP repos use Composer + Pest instead of Go + Task. Verify:
- [ ] CI triggers correctly (different workflow)
- [ ] Agent understands PHP codebase (Pest tests, Pint formatting)
- [ ] `lang:php` label applied to issues
- [ ] Epic branch naming works the same way
---
## Monitoring
### Daily Check
```bash
# Quick status across all repos with epics
for REPO in core core-php core-mcp core-api; do
OPEN=$(gh issue list --repo dappcore/$REPO --state open --json number --jq 'length')
PRS=$(gh pr list --repo dappcore/$REPO --state open --json number --jq 'length')
echo "$REPO: $OPEN open issues, $PRS open PRs"
done
```
### Epic Progress
```bash
# Check epic completion per repo
EPIC=299
REPO="dappcore/core"
gh issue view $EPIC --repo $REPO --json body --jq '
.body | split("\n") | map(select(test("^- \\[[ x]\\] #"))) |
{ total: length,
done: map(select(test("^- \\[x\\] #"))) | length,
remaining: map(select(test("^- \\[ \\] #"))) | length }'
```
### Agent Quota
```bash
# No API for Jules quota — track manually
# Record dispatches in a local file
echo "$(date -u +%Y-%m-%dT%H:%MZ) dispatched #ISSUE to jules in REPO" >> .core/dispatch.log
wc -l .core/dispatch.log # count today's dispatches
```
---
## Budget Tracking & Continuous Flow
The goal is to keep agents working at all times — never idle, never over-budget. Every team member who connects their repo to Jules gets 300 tasks/day. The orchestrator should use the full team allowance.
### Team Budget Pool
Each team member with a Jules-enabled repo contributes to the daily pool:
| Member | Repos Connected | Daily Quota | Notes |
|--------|----------------|-------------|-------|
| @Snider | core, core-php, core-mcp, core-api, ... | 300 | Primary orchestrator |
| @bodane | (to be connected) | 300 | Code owner |
| (additional members) | (additional repos) | 300 | Per-member quota |
**Total pool = members x 300 tasks/day.** With 2 members: 600 tasks/day.
### Budget Tracking
**Preferred:** Use the Jules CLI for accurate, real-time budget info:
```bash
# Get current usage (when Jules CLI is available)
jules usage # Shows today's task count and remaining quota
jules usage --team # Shows per-member breakdown
```
**Fallback:** Track dispatches in a structured log:
```bash
# Dispatch log format (append-only)
# TIMESTAMP REPO ISSUE AGENT EPIC
echo "$(date -u +%Y-%m-%dT%H:%MZ) core-mcp #29 jules #EPIC" >> .core/dispatch.log
# Today's usage
TODAY=$(date -u +%Y-%m-%d)
grep "$TODAY" .core/dispatch.log | wc -l
# Remaining budget
USED=$(grep "$TODAY" .core/dispatch.log | wc -l)
POOL=300 # multiply by team size
echo "Used: $USED / $POOL Remaining: $((POOL - USED))"
```
**Don't guess the budget.** Either query the CLI or count dispatches. Manual estimates drift.
### Continuous Flow Strategy
The orchestrator should maintain a **pipeline of ready work** so agents are never idle. The flow looks like this:
```
BACKLOG READY DISPATCHED IN PROGRESS DONE
───────── ───── ────────── ─────────── ────
Audit issues → Implementation → Labelled for → Agent working → PR merged
(unprocessed) issues in epics agent pickup on PR child closed
```
**Key metric: READY queue depth.** If the READY queue is empty, agents will idle when current work finishes. The orchestrator should always maintain 2-3x the daily dispatch rate in READY state.
### Dispatch Cadence
```
Morning (start of day):
1. Check yesterday's results — tick parent checklists for merged PRs
2. Check remaining budget from yesterday (unused tasks don't roll over)
3. Unstick any blocked PRs (merge conflicts → resolve-stuck-prs flow after 2+ attempts, unresolved threads)
4. Dispatch Phase 1 blockers for new epics (if budget allows)
5. Dispatch next-phase children for epics where phase completed
Midday (check-in):
6. Check for new merge conflicts from cascade merges
7. Send "fix the merge conflict" / "fix the code reviews" as needed
8. Dispatch more children if budget remains and agents are idle
Evening (wind-down):
9. Review day's throughput: dispatched vs merged vs stuck
10. Plan tomorrow's dispatch based on remaining backlog
11. Run Stage 1/2 on new repos to refill READY queue
```
### Auto-Dispatch Rules
When the orchestrator detects a child issue was completed (merged + closed):
1. Tick the parent checklist
2. Check if the completed phase is now done (all children in phase closed)
3. If phase done → dispatch next phase's children
4. If epic done → merge epic branch to dev, close epic, dispatch next epic
5. Log the dispatch in the budget tracker
```bash
# Detect completed children (structural only)
EPIC=299
REPO="dappcore/core"
# Get unchecked children
UNCHECKED=$(gh issue view $EPIC --repo $REPO --json body --jq '
[.body | split("\n")[] | select(test("^- \\[ \\] #")) |
capture("^- \\[ \\] #(?<num>[0-9]+)") | .num] | .[]')
# Check which are actually closed
for CHILD in $UNCHECKED; do
STATE=$(gh issue view $CHILD --repo $REPO --json state --jq '.state')
if [ "$STATE" = "CLOSED" ]; then
echo "Child #$CHILD is closed but unchecked — tick parent and dispatch next"
fi
done
```
### Filling the Pipeline
To ensure agents always have work:
| When | Action |
|------|--------|
| READY queue < 20 issues | Run Stage 1 on next Tier repo |
| All Tier 1 repos have epics | Move to Tier 2 |
| All audits processed | Run new audits (`[Audit]` issue sweep) |
| Epic completes | Merge branch, dispatch next epic in same repo |
| Daily budget < 50% used by midday | Increase dispatch rate |
| Daily budget > 80% used by morning | Throttle, focus on unsticking |
### Multi-Repo Dispatch Balancing
With multiple repos in flight, balance dispatches across repos to avoid bottlenecks:
```
Priority order for dispatch:
1. Critical/High severity children (security fixes first)
2. Repos with most work remaining (maximise throughput)
3. Children with no dependencies (parallelisable)
4. Repos with CI most likely to pass (lower retry cost)
```
**Never dispatch all budget to one repo.** If `core-php` has 50 children, don't dispatch all 50 today. Spread across repos:
```
Example daily plan (300 budget):
core: 10 tasks (unstick 2 PRs + dispatch 8 new)
core-php: 40 tasks (Phase 1 security epic)
core-mcp: 30 tasks (workspace isolation epic)
core-api: 20 tasks (webhook security epic)
Remaining: 200 tasks (Tier 2-4 repos or iteration on above)
```
### Team Onboarding
When a new team member connects their repos:
1. Add their repos to the inventory table
2. Update the pool total (+300/day)
3. Run Stage 1-2 on their repos
4. Include their repos in the dispatch balancing
```bash
# Track team members and their quotas
cat <<'EOF' >> .core/team.yaml
members:
- login: Snider
quota: 300
repos: [core, core-php, core-mcp, core-api, core-admin, core-tenant,
core-developer, core-service-commerce, core-devops, core-template,
build, ansible-coolify, docker-server-php, docker-server-blockchain]
- login: bodane
quota: 300
repos: [] # to be connected
EOF
```
### `core dev budget` Command
```bash
core dev budget # Show today's usage vs pool
core dev budget --plan # Suggest optimal dispatch plan for today
core dev budget --history # Daily usage over past week
core dev budget --team # Show per-member quota and usage
core dev budget --forecast DAYS # Project when all epics will complete
```
---
## Failure Modes
| Failure | Detection | Recovery |
|---------|-----------|----------|
| Audit has no actionable findings | Stage 1 produces 0 issues | Close audit as "not applicable" |
| Too few issues for epic (< 3) | Stage 2 grouping | Dispatch directly, skip epic |
| Agent can't handle PHP/Go | PR fails CI repeatedly | Re-assign to different model or human |
| Cascade conflicts | Multiple PRs stuck CONFLICTING | Serialise merges, use epic branch |
| Agent quota exhausted | 300 tasks hit | Wait for daily reset, prioritise |
| Repo has no CI | PRs can't pass checks | Skip CI gate, rely on reviews only |
| Epic branch diverges too far from dev | Merge conflicts on epic → dev | Rebase epic branch periodically |
---
## Quick Reference
```
1. AUDIT → Run audit-issues flow per repo (free, parallelisable)
2. ORGANISE → Run create-epic flow per repo (free, parallelisable)
3. DISPATCH → Add jules label to Phase 1 blockers (costs quota)
4. MONITOR → Run issue-epic flow per epic (ongoing)
5. COMPLETE → Merge epic branch to dev, close epic
```
---
---
*Companion to: RFC.flow-audit-issues.md, RFC.flow-create-epic.md, RFC.flow-issue-epic.md*

View file

@ -0,0 +1,174 @@
---
name: flow-resolve-stuck-prs
description: Use when a PR is stuck CONFLICTING after 2+ failed agent attempts. Manual merge conflict resolution using git worktrees.
---
# Flow: Resolve Stuck PRs
Manually resolve merge conflicts when an implementer has failed to fix them after two attempts, and the PR(s) are the last items blocking an epic.
---
## When to Use
All three conditions must be true:
1. **PR is CONFLICTING/DIRTY** after the implementer was asked to fix it (at least twice)
2. **The PR is blocking epic completion** — it's one of the last unchecked children
3. **No other approach worked** — "Can you fix the merge conflict?" was sent and either got no response or the push still left conflicts
## Inputs
- **Repo**: `owner/repo`
- **PR numbers**: The stuck PRs (e.g. `#287, #291`)
- **Target branch**: The branch the PRs target (e.g. `dev`, `epic/101-medium-migration`)
## Process
### Step 1: Confirm Stuck Status
Verify each PR is genuinely stuck — not just slow.
```bash
for PR in 287 291; do
echo "=== PR #$PR ==="
gh pr view $PR --repo OWNER/REPO --json mergeable,mergeStateStatus,updatedAt \
--jq '{mergeable, mergeStateStatus, updatedAt}'
done
```
**Skip if:** `mergeStateStatus` is not `DIRTY` — the PR isn't actually conflicting.
### Step 2: Check Attempt History
Count how many times the implementer was asked and whether it responded.
```bash
# Count "fix the merge conflict" comments
gh pr view $PR --repo OWNER/REPO --json comments \
--jq '[.comments[] | select(.body | test("merge conflict"; "i"))] | length'
# Check last commit date vs last conflict request
gh pr view $PR --repo OWNER/REPO --json commits \
--jq '.commits[-1] | {sha: .oid[:8], date: .committedDate}'
```
**Proceed only if:** 2+ conflict fix requests were sent AND either:
- No commit after the last request (implementer didn't respond), OR
- A commit was pushed but `mergeStateStatus` is still `DIRTY` (fix attempt failed)
### Step 3: Clone and Resolve Locally
Task a single agent (or do it manually) to resolve conflicts for ALL stuck PRs in one session.
```bash
# Ensure we have the latest
git fetch origin
# For each stuck PR
for PR in 287 291; do
BRANCH=$(gh pr view $PR --repo OWNER/REPO --json headRefName --jq '.headRefName')
TARGET=$(gh pr view $PR --repo OWNER/REPO --json baseRefName --jq '.baseRefName')
git checkout "$BRANCH"
git pull origin "$BRANCH"
# Merge target branch into PR branch
git merge "origin/$TARGET" --no-edit
# If conflicts exist, resolve them
# Agent should: read each conflicted file, choose the correct resolution,
# stage the resolved files, and commit
git add -A
git commit -m "chore: resolve merge conflicts with $TARGET"
git push origin "$BRANCH"
done
```
**Agent instructions when dispatching:**
> Resolve the merge conflicts on PR #X, #Y, #Z in `owner/repo`.
> For each PR: checkout the PR branch, merge the target branch, resolve all conflicts
> preserving the intent of both sides, commit, and push.
> If a conflict is ambiguous (both sides changed the same logic in incompatible ways),
> prefer the target branch version and note what you dropped in the commit message.
### Step 4: Verify Resolution
After pushing, confirm the PR is no longer conflicting.
```bash
# Wait a few seconds for GitHub to recalculate
sleep 10
for PR in 287 291; do
STATUS=$(gh pr view $PR --repo OWNER/REPO --json mergeStateStatus --jq '.mergeStateStatus')
echo "PR #$PR: $STATUS"
done
```
**Expected:** `CLEAN` or `BLOCKED` (waiting for checks, not conflicts).
### Step 5: Handle Failure
If the PR is **still conflicting** after manual resolution:
```bash
# Label for human intervention
gh issue edit $PR --repo OWNER/REPO --add-label "needs-intervention"
# Comment for the gatekeeper
gh pr comment $PR --repo OWNER/REPO \
--body "Automated conflict resolution failed after 2+ implementer attempts and 1 manual attempt. Needs human review."
```
Create the label if it doesn't exist:
```bash
gh label create "needs-intervention" --repo OWNER/REPO \
--description "Automated resolution failed — needs human review" \
--color "B60205" 2>/dev/null
```
The orchestrator should then **skip this PR** and continue with other epic children. Don't block the entire epic on one stuck PR.
---
## Decision Flowchart
```
PR is CONFLICTING
└─ Was implementer asked to fix? (check comment history)
├─ No → Send "Can you fix the merge conflict?" (issue-epic flow)
└─ Yes, 1 time → Send again, wait for response
└─ Yes, 2+ times → THIS FLOW
└─ Agent resolves locally
├─ Success → PR clean, pipeline continues
└─ Failure → Label `needs-intervention`, skip PR
```
## Dispatching as a Subagent
When the orchestrator detects a PR matching the trigger conditions, it can dispatch this flow as a single task:
```
Resolve merge conflicts on PRs #287 and #291 in dappcore/core.
Both PRs target `dev`. The implementer was asked to fix conflicts 2+ times
but they remain DIRTY. Check out each PR branch, merge origin/dev, resolve
all conflicts, commit, and push. If any PR can't be resolved, add the
`needs-intervention` label.
```
**Cost:** 0 Jules tasks (this runs locally or via Claude Code, not via Jules label).
---
## Integration
**Called by:** `issue-epic.md` — when a PR has been CONFLICTING for 2+ fix attempts
**Calls:** Nothing — this is a terminal resolution flow
**Fallback:** `needs-intervention` label → human gatekeeper reviews manually
---
*Created: 2026-02-04*
*Companion to: RFC.flow-issue-epic.md*

255
docs/flow/RFC.md Normal file
View file

@ -0,0 +1,255 @@
# core/agent/flow RFC — YAML-Defined Agent Workflows
> The authoritative spec for the Flow system — declarative, composable, path-addressed agent workflows.
> No code changes needed to improve agent capability. Just YAML + rebuild.
**Package:** `core/agent` (pkg/lib/flow/)
**Repository:** `dappco.re/go/agent`
**Related:** Pipeline Orchestration (core/agent/RFC.pipeline.md)
---
## 1. Overview
Flows are YAML definitions of agent workflows — tasks, prompts, verification steps, security gates. They're composable: flows call other flows. They're path-addressed: the file path IS the semantic meaning.
### 1.1 Design Principle
**Path = semantics.** The same principle as dAppServer's unified path convention:
```
flow/deploy/from/forge.yaml ← pull from Forge
flow/deploy/to/forge.yaml ← push to Forge (opposite direction)
flow/workspace/prepare/go.yaml
flow/workspace/prepare/php.yaml
flow/workspace/prepare/devops.yaml
```
An agent navigating by path shouldn't need a README to find the right flow.
### 1.2 Why This Matters
- **Scales without code:** Add a flow YAML, rebuild, done. 20 repos → 200 repos with same effort.
- **Separates what from how:** Flow YAML = intent (what to do). Go code = mechanics (how to do it).
- **Self-healing:** Every problem encountered improves the flow. DevOps lifecycle: hit problem → fix flow → automated forever.
- **Autonomous pipeline:** Issue opened → PR ready for review, without human or orchestrator touching it.
---
## 2. Flow Structure
### 2.1 Basic Flow
```yaml
# flow/verify/go-qa.yaml
name: Go QA
description: Build, test, vet, lint a Go project
steps:
- name: build
run: go build ./...
- name: test
run: go test ./...
- name: vet
run: go vet ./...
- name: lint
run: golangci-lint run
```
### 2.2 Composed Flow
Flows call other flows via `flow:` directive:
```yaml
# flow/implement/security-scan.yaml
name: Security Scan Implementation
description: Full lifecycle — prepare, plan, implement, verify, PR
steps:
- name: prepare
flow: workspace/prepare/go.yaml
- name: plan
agent: spark
prompt: "Create a security scan implementation plan"
- name: implement
agent: codex
prompt: "Implement the plan"
- name: verify
flow: verify/go-qa.yaml
- name: pr
flow: pr/to-dev.yaml
```
### 2.3 Agent Steps
Steps can dispatch agents with specific prompts:
```yaml
- name: implement
agent: codex # Agent type
prompt: | # Task prompt
Read CODEX.md and the RFC at .core/reference/docs/RFC.md.
Implement the security scan findings.
template: coding # Prompt template
timeout: 30m # Max runtime
```
### 2.4 Conditional Steps
```yaml
- name: check-language
run: cat .core/manifest.yaml | grep language
output: language
- name: go-verify
flow: verify/go-qa.yaml
when: "{{ .language == 'go' }}"
- name: php-verify
flow: verify/php-qa.yaml
when: "{{ .language == 'php' }}"
```
---
## 3. Path Convention
### 3.1 Directory Layout
```
pkg/lib/flow/
├── deploy/
│ ├── from/
│ │ └── forge.yaml # Pull from Forge
│ └── to/
│ ├── forge.yaml # Push to Forge
│ └── github.yaml # Push to GitHub
├── implement/
│ ├── security-scan.yaml
│ └── upgrade-deps.yaml
├── pr/
│ ├── to-dev.yaml # Create PR to dev branch
│ └── to-main.yaml # Create PR to main branch
├── upgrade/
│ ├── v080-plan.yaml # Plan v0.8.0 upgrade
│ └── v080-implement.yaml # Implement v0.8.0 upgrade
├── verify/
│ ├── go-qa.yaml # Go build+test+vet+lint
│ └── php-qa.yaml # PHP pest+pint+phpstan
└── workspace/
└── prepare/
├── go.yaml # Prepare Go workspace
├── php.yaml # Prepare PHP workspace
├── ts.yaml # Prepare TypeScript workspace
├── devops.yaml # Prepare DevOps workspace
└── secops.yaml # Prepare SecOps workspace
```
### 3.2 Naming Rules
- **Verbs first:** `deploy/`, `implement/`, `verify/`, `prepare/`
- **Direction explicit:** `from/forge` vs `to/forge`
- **Language suffixed:** `verify/go-qa` vs `verify/php-qa`
- **No abbreviations:** `workspace` not `ws`, `implement` not `impl`
---
## 4. Execution Model
### 4.1 Flow Runner
The Go runner in `pkg/lib/flow/` executes flows:
1. Load YAML flow definition
2. Resolve `flow:` references (recursive)
3. Execute steps sequentially
4. Capture output variables
5. Evaluate `when:` conditions
6. Dispatch agents via Core IPC (runner.dispatch Action)
7. Collect results
### 4.2 CLI Interface
```bash
# Run a flow directly
core-agent run flow pkg/lib/flow/verify/go-qa.yaml
# Dry-run (show what would execute)
core-agent run flow pkg/lib/flow/verify/go-qa.yaml --dry-run
# Run with variables
core-agent run flow pkg/lib/flow/upgrade/v080-implement.yaml --var repo=core/go
```
---
## 5. Composition Patterns
### 5.1 Pipeline (sequential)
```yaml
steps:
- flow: workspace/prepare/go.yaml
- flow: verify/go-qa.yaml
- flow: pr/to-dev.yaml
```
### 5.2 Fan-out (parallel repos)
```yaml
steps:
- name: upgrade-all
parallel:
- flow: upgrade/v080-implement.yaml
var: { repo: core/go }
- flow: upgrade/v080-implement.yaml
var: { repo: core/go-io }
- flow: upgrade/v080-implement.yaml
var: { repo: core/go-log }
```
### 5.3 Gate (human approval)
```yaml
steps:
- flow: implement/security-scan.yaml
- name: review-gate
gate: manual
prompt: "Security scan complete. Review PR before merge?"
- flow: pr/merge.yaml
```
---
## 6. End State
core-agent CLI runs as a native Forge runner:
1. Forge webhook fires (issue created, PR updated, push event)
2. core-agent picks up the event
3. Selects appropriate flow based on event type + repo config
4. Runs flow → handles full lifecycle
5. No GitHub Actions, no external CI
6. All compute on our hardware
7. Every problem encountered → flow improvement → automated forever
---
## 7. Reference Material
| Resource | Location |
|----------|----------|
| **core/agent** | Flows dispatch agents via Core IPC |
| **core/agent/plugins** | Flows reference agent types (codex, spark, claude) |
| **dAppServer** | Unified path convention = same design principle |
| **core/config** | .core/ convention for workspace detection |
---
## Changelog
- 2026-03-27: Initial RFC promoted from memory + existing flow files. Path-addressed, composable, declarative.

View file

@ -0,0 +1,111 @@
# core/php/agent — Actions
## Brain
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Remember | `Actions\Brain\RememberKnowledge` | content, tags[], project? | BrainMemory |
| Recall | `Actions\Brain\RecallKnowledge` | query, limit?, tags[]? | BrainMemory[] |
| Forget | `Actions\Brain\ForgetKnowledge` | id | bool |
| List | `Actions\Brain\ListKnowledge` | filters? | BrainMemory[] |
## Forge
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Assign Agent | `Actions\Forge\AssignAgent` | issue_id, agent_type | bool |
| Create Plan from Issue | `Actions\Forge\CreatePlanFromIssue` | issue_id | AgentPlan |
| Manage PR | `Actions\Forge\ManagePullRequest` | pr_id, action | bool |
| Report to Issue | `Actions\Forge\ReportToIssue` | issue_id, report | bool |
| Scan for Work | `Actions\Forge\ScanForWork` | — | Issue[] |
## Plan
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Create | `Actions\Plan\CreatePlan` | title, description, phases[] | AgentPlan |
| Get | `Actions\Plan\GetPlan` | id or slug | AgentPlan |
| List | `Actions\Plan\ListPlans` | status?, workspace_id? | AgentPlan[] |
| Update Status | `Actions\Plan\UpdatePlanStatus` | id, status | AgentPlan |
| Archive | `Actions\Plan\ArchivePlan` | id | bool |
## Session
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Start | `Actions\Session\StartSession` | agent_type, plan_id?, context | AgentSession |
| Continue | `Actions\Session\ContinueSession` | session_id, work_log | AgentSession |
| End | `Actions\Session\EndSession` | session_id, summary, handoff? | AgentSession |
| Get | `Actions\Session\GetSession` | session_id | AgentSession |
| List | `Actions\Session\ListSessions` | status?, agent_type? | AgentSession[] |
## Issue
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Create | `Actions\Issue\CreateIssue` | title, type, priority, labels[] | Issue |
| Get | `Actions\Issue\GetIssue` | id | Issue |
| List | `Actions\Issue\ListIssues` | status?, type?, sprint_id? | Issue[] |
| Update | `Actions\Issue\UpdateIssue` | id, fields | Issue |
| Comment | `Actions\Issue\AddIssueComment` | issue_id, body | IssueComment |
| Archive | `Actions\Issue\ArchiveIssue` | id | bool |
## Sprint
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Create | `Actions\Sprint\CreateSprint` | title, goal, started_at, ended_at | Sprint |
| Get | `Actions\Sprint\GetSprint` | id | Sprint |
| List | `Actions\Sprint\ListSprints` | status? | Sprint[] |
| Update | `Actions\Sprint\UpdateSprint` | id, fields | Sprint |
| Archive | `Actions\Sprint\ArchiveSprint` | id | bool |
## Phase
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Get | `Actions\Phase\GetPhase` | id | AgentPhase |
| Update Status | `Actions\Phase\UpdatePhaseStatus` | id, status | AgentPhase |
| Add Checkpoint | `Actions\Phase\AddCheckpoint` | id, checkpoint_data | AgentPhase |
## Task
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Toggle | `Actions\Task\ToggleTask` | id | Task |
| Update | `Actions\Task\UpdateTask` | id, fields | Task |
## Auth
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| ProvisionKey | `Actions\Auth\ProvisionAgentKey` | oauth_user_id, name?, permissions[]? | AgentApiKey |
| RevokeKey | `Actions\Auth\RevokeAgentKey` | key_id | bool |
## Fleet
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| Register | `Actions\Fleet\RegisterNode` | agent_id, capabilities, platform, models[] | FleetNode |
| Heartbeat | `Actions\Fleet\NodeHeartbeat` | agent_id, status, compute_budget | FleetNode |
| Deregister | `Actions\Fleet\DeregisterNode` | agent_id | bool |
| ListNodes | `Actions\Fleet\ListNodes` | status?, platform? | FleetNode[] |
| AssignTask | `Actions\Fleet\AssignTask` | agent_id, task, repo, template | FleetTask |
| CompleteTask | `Actions\Fleet\CompleteTask` | agent_id, task_id, result, findings[] | FleetTask (triggers AwardCredits as side-effect) |
| GetNextTask | `Actions\Fleet\GetNextTask` | agent_id, capabilities | FleetTask? (scheduler: P0-P3 priority, capability match, repo affinity, round-robin, budget check) |
## Fleet Stats
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| GetFleetStats | `Actions\Fleet\GetFleetStats` | (none) | FleetStats |
## Sync
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| PushState | `Actions\Sync\PushDispatchHistory` | agent_id, dispatches[] | SyncResult |
| PullContext | `Actions\Sync\PullFleetContext` | agent_id, since? | FleetContext |
| GetStatus | `Actions\Sync\GetAgentSyncStatus` | agent_id | SyncStatus |
## Credits
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| AwardCredits | `Actions\Credits\AwardCredits` | agent_id, task_type, amount | CreditEntry |
| GetBalance | `Actions\Credits\GetBalance` | agent_id | CreditBalance |
| GetHistory | `Actions\Credits\GetCreditHistory` | agent_id, limit? | CreditEntry[] |
## Subscription
| Action | Class | Input | Output |
|--------|-------|-------|--------|
| DetectCapabilities | `Actions\Subscription\DetectCapabilities` | api_keys{} | Capabilities |
| GetNodeBudget | `Actions\Subscription\GetNodeBudget` | agent_id | Budget |
| UpdateBudget | `Actions\Subscription\UpdateBudget` | agent_id, limits | Budget |

View file

@ -0,0 +1,322 @@
---
title: Architecture
description: Technical architecture of the core-agentic package
updated: 2026-01-29
---
# Architecture
The `core-agentic` package provides AI agent orchestration infrastructure for the Host platform. It enables multi-agent collaboration, persistent task tracking, and unified access to multiple AI providers.
## Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ MCP Protocol Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Plan │ │ Phase │ │ Session │ │ State │ ... tools │
│ │ Tools │ │ Tools │ │ Tools │ │ Tools │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼────────────┼────────────┼────────────┼──────────────────┘
│ │ │ │
┌───────┴────────────┴────────────┴────────────┴──────────────────┐
│ AgentToolRegistry │
│ - Tool registration and discovery │
│ - Permission checking (API key scopes) │
│ - Dependency validation │
│ - Circuit breaker integration │
└──────────────────────────────────────────────────────────────────┘
┌───────┴──────────────────────────────────────────────────────────┐
│ Core Services │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ AgenticManager │ │ AgentApiKey │ │ PlanTemplate │ │
│ │ (AI Providers) │ │ Service │ │ Service │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ IpRestriction │ │ Content │ │ AgentSession │ │
│ │ Service │ │ Service │ │ Service │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
┌───────┴──────────────────────────────────────────────────────────┐
│ Data Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐│
│ │ AgentPlan │ │ AgentPhase │ │ AgentSession│ │ AgentApiKey ││
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘│
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Workspace │ │ Task │ │
│ │ State │ │ │ │
│ └─────────────┘ └─────────────┘ │
└──────────────────────────────────────────────────────────────────┘
```
## Core Concepts
### Agent Plans
Plans represent structured work with phases, tasks, and progress tracking. They persist across agent sessions, enabling handoff between different AI models or instances.
```
AgentPlan
├── slug (unique identifier)
├── title
├── status (draft → active → completed/archived)
├── current_phase
└── phases[] (AgentPhase)
├── name
├── tasks[]
│ ├── name
│ └── status
├── dependencies[]
└── checkpoints[]
```
**Lifecycle:**
1. Created via MCP tool or template
2. Activated to begin work
3. Phases started/completed in order
4. Plan auto-completes when all phases done
5. Archived for historical reference
### Agent Sessions
Sessions track individual work periods. They enable context recovery when an agent's context window resets or when handing off to another agent.
```
AgentSession
├── session_id (prefixed unique ID)
├── agent_type (opus/sonnet/haiku)
├── status (active/paused/completed/failed)
├── work_log[] (chronological actions)
├── artifacts[] (files created/modified)
├── context_summary (current state)
└── handoff_notes (for next agent)
```
**Handoff Flow:**
1. Session logs work as it progresses
2. Before context ends, agent calls `session_handoff`
3. Handoff notes capture summary, next steps, blockers
4. Next agent calls `session_resume` to continue
5. Resume session inherits context from previous
### Workspace State
Key-value state storage shared between sessions and plans. Enables agents to persist decisions, configurations, and intermediate results.
```
WorkspaceState
├── key (namespaced identifier)
├── value (any JSON-serialisable data)
├── type (json/markdown/code/reference)
└── category (for organisation)
```
## MCP Tool Architecture
All MCP tools extend the `AgentTool` base class which provides:
### Input Validation
```php
protected function requireString(array $args, string $key, ?int $maxLength = null): string
protected function optionalInt(array $args, string $key, ?int $default = null): ?int
protected function requireEnum(array $args, string $key, array $allowed): string
```
### Circuit Breaker Protection
```php
return $this->withCircuitBreaker('agentic', function () {
// Database operations that could fail
return AgentPlan::where('slug', $slug)->first();
}, fn () => $this->error('Service unavailable', 'circuit_open'));
```
### Dependency Declaration
```php
public function dependencies(): array
{
return [
ToolDependency::contextExists('workspace_id', 'Workspace required'),
ToolDependency::toolCalled('session_start', 'Start session first'),
];
}
```
### Tool Categories
| Category | Tools | Purpose |
|----------|-------|---------|
| `plan` | plan_create, plan_get, plan_list, plan_update_status, plan_archive | Work plan management |
| `phase` | phase_get, phase_update_status, phase_add_checkpoint | Phase operations |
| `session` | session_start, session_end, session_log, session_handoff, session_resume, session_replay | Session tracking |
| `state` | state_get, state_set, state_list | Persistent state |
| `task` | task_update, task_toggle | Task completion |
| `template` | template_list, template_preview, template_create_plan | Plan templates |
| `content` | content_generate, content_batch_generate, content_brief_create | Content generation |
## AI Provider Abstraction
The `AgenticManager` provides unified access to multiple AI providers:
```php
$ai = app(AgenticManager::class);
// Use specific provider
$response = $ai->claude()->generate($system, $user);
$response = $ai->gemini()->generate($system, $user);
$response = $ai->openai()->generate($system, $user);
// Use by name (for configuration-driven selection)
$response = $ai->provider('gemini')->generate($system, $user);
```
### Provider Interface
All providers implement `AgenticProviderInterface`:
```php
interface AgenticProviderInterface
{
public function generate(string $systemPrompt, string $userPrompt, array $config = []): AgenticResponse;
public function stream(string $systemPrompt, string $userPrompt, array $config = []): Generator;
public function name(): string;
public function defaultModel(): string;
public function isAvailable(): bool;
}
```
### Response Object
```php
class AgenticResponse
{
public string $content;
public string $model;
public int $inputTokens;
public int $outputTokens;
public int $durationMs;
public ?string $stopReason;
public array $raw;
public function estimateCost(): float;
}
```
## Authentication
### API Key Flow
```
Request → AgentApiAuth Middleware → AgentApiKeyService::authenticate()
├── Validate key (SHA-256 hash lookup)
├── Check revoked/expired
├── Validate IP whitelist
├── Check permissions
├── Check rate limit
└── Record usage
```
### Permission Model
```php
// Permission constants
AgentApiKey::PERM_PLANS_READ // 'plans.read'
AgentApiKey::PERM_PLANS_WRITE // 'plans.write'
AgentApiKey::PERM_SESSIONS_WRITE // 'sessions.write'
// etc.
// Check permissions
$key->hasPermission('plans.write');
$key->hasAllPermissions(['plans.read', 'sessions.write']);
```
### IP Restrictions
API keys can optionally restrict access by IP:
- Individual IPv4/IPv6 addresses
- CIDR notation (e.g., `192.168.1.0/24`)
- Mixed whitelist
## Event-Driven Boot
The module uses the Core framework's event-driven lazy loading:
```php
class Boot extends ServiceProvider
{
public static array $listens = [
AdminPanelBooting::class => 'onAdminPanel',
ConsoleBooting::class => 'onConsole',
McpToolsRegistering::class => 'onMcpTools',
];
}
```
This ensures:
- Views only loaded when admin panel boots
- Commands only registered when console boots
- MCP tools only registered when MCP module initialises
## Multi-Tenancy
All data is workspace-scoped via the `BelongsToWorkspace` trait:
- Queries auto-scoped to current workspace
- Creates auto-assign workspace_id
- Cross-tenant queries throw `MissingWorkspaceContextException`
## File Structure
```
core-agentic/
├── Boot.php # Service provider with event handlers
├── config.php # Module configuration
├── Migrations/ # Database schema
├── Models/ # Eloquent models
│ ├── AgentPlan.php
│ ├── AgentPhase.php
│ ├── AgentSession.php
│ ├── AgentApiKey.php
│ └── WorkspaceState.php
├── Services/ # Business logic
│ ├── AgenticManager.php # AI provider orchestration
│ ├── AgentApiKeyService.php # API key management
│ ├── IpRestrictionService.php
│ ├── PlanTemplateService.php
│ ├── ContentService.php
│ ├── ClaudeService.php
│ ├── GeminiService.php
│ └── OpenAIService.php
├── Mcp/
│ ├── Tools/Agent/ # MCP tool implementations
│ │ ├── AgentTool.php # Base class
│ │ ├── Plan/
│ │ ├── Phase/
│ │ ├── Session/
│ │ ├── State/
│ │ └── ...
│ ├── Prompts/ # MCP prompt definitions
│ └── Servers/ # MCP server configurations
├── Middleware/
│ └── AgentApiAuth.php # API authentication
├── Controllers/
│ └── ForAgentsController.php # Agent discovery endpoint
├── View/
│ ├── Blade/admin/ # Admin panel views
│ └── Modal/Admin/ # Livewire components
├── Jobs/ # Queue jobs
├── Console/Commands/ # Artisan commands
└── Tests/ # Pest test suites
```
## Dependencies
- `dappcore/core` - Event system, base classes
- `dappcore/core-tenant` - Workspace, BelongsToWorkspace trait
- `dappcore/core-mcp` - MCP infrastructure, CircuitBreaker

View file

@ -0,0 +1,14 @@
# core/php/agent — Console Commands
| Command | Artisan | Schedule | Purpose |
|---------|---------|----------|---------|
| `TaskCommand` | `agentic:task` | — | Manage tasks (create, update, toggle) |
| `PlanCommand` | `agentic:plan` | — | Manage plans (create from template, status) |
| `GenerateCommand` | `agentic:generate` | — | AI content generation |
| `PlanRetentionCommand` | `agentic:plan-cleanup` | Daily | Archive old completed plans |
| `BrainSeedMemoryCommand` | `brain:seed-memory` | — | Seed brain from file/directory |
| `BrainIngestCommand` | `brain:ingest` | — | Bulk ingest memories |
| `ScanCommand` | `agentic:scan` | Every 5 min | Scan Forge for actionable issues |
| `DispatchCommand` | `agentic:dispatch` | Every 2 min | Dispatch queued agents |
| `PrManageCommand` | `agentic:pr-manage` | Every 5 min | Manage open PRs (merge/close/review) |
| `PrepWorkspaceCommand` | `agentic:prep-workspace` | — | Prepare sandboxed workspace for agent |

View file

@ -0,0 +1,670 @@
---
title: MCP Tools Reference
description: Complete reference for core-agentic MCP tools
updated: 2026-01-29
---
# MCP Tools Reference
This document provides a complete reference for all MCP tools in the `core-agentic` package.
## Overview
Tools are organised into categories:
| Category | Description | Tools Count |
|----------|-------------|-------------|
| plan | Work plan management | 5 |
| phase | Phase operations | 3 |
| session | Session tracking | 8 |
| state | Persistent state | 3 |
| task | Task completion | 2 |
| template | Plan templates | 3 |
| content | Content generation | 6 |
## Plan Tools
### plan_create
Create a new work plan with phases and tasks.
**Scopes:** `write`
**Input:**
```json
{
"title": "string (required)",
"slug": "string (optional, auto-generated)",
"description": "string (optional)",
"context": "object (optional)",
"phases": [
{
"name": "string",
"description": "string",
"tasks": ["string"]
}
]
}
```
**Output:**
```json
{
"success": true,
"plan": {
"slug": "my-plan-abc123",
"title": "My Plan",
"status": "draft",
"phases": 3
}
}
```
**Dependencies:** workspace_id in context
---
### plan_get
Get a plan by slug with full details.
**Scopes:** `read`
**Input:**
```json
{
"slug": "string (required)"
}
```
**Output:**
```json
{
"success": true,
"plan": {
"slug": "my-plan",
"title": "My Plan",
"status": "active",
"progress": {
"total": 5,
"completed": 2,
"percentage": 40
},
"phases": [...]
}
}
```
---
### plan_list
List plans with optional filtering.
**Scopes:** `read`
**Input:**
```json
{
"status": "string (optional: draft|active|completed|archived)",
"limit": "integer (optional, default 20)"
}
```
**Output:**
```json
{
"success": true,
"plans": [
{
"slug": "plan-1",
"title": "Plan One",
"status": "active"
}
],
"count": 1
}
```
---
### plan_update_status
Update a plan's status.
**Scopes:** `write`
**Input:**
```json
{
"slug": "string (required)",
"status": "string (required: draft|active|completed|archived)"
}
```
---
### plan_archive
Archive a plan with optional reason.
**Scopes:** `write`
**Input:**
```json
{
"slug": "string (required)",
"reason": "string (optional)"
}
```
## Phase Tools
### phase_get
Get phase details by plan slug and phase order.
**Scopes:** `read`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)"
}
```
---
### phase_update_status
Update a phase's status.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)",
"status": "string (required: pending|in_progress|completed|blocked|skipped)",
"reason": "string (optional, for blocked/skipped)"
}
```
---
### phase_add_checkpoint
Add a checkpoint note to a phase.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)",
"note": "string (required)",
"context": "object (optional)"
}
```
## Session Tools
### session_start
Start a new agent session.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (optional)",
"agent_type": "string (required: opus|sonnet|haiku)",
"context": "object (optional)"
}
```
**Output:**
```json
{
"success": true,
"session": {
"session_id": "ses_abc123xyz",
"agent_type": "opus",
"plan": "my-plan",
"status": "active"
}
}
```
---
### session_end
End a session with status and summary.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"status": "string (required: completed|failed)",
"summary": "string (optional)"
}
```
---
### session_log
Add a work log entry to an active session.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"message": "string (required)",
"type": "string (optional: info|warning|error|success|checkpoint)",
"data": "object (optional)"
}
```
---
### session_handoff
Prepare session for handoff to another agent.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"summary": "string (required)",
"next_steps": ["string"],
"blockers": ["string"],
"context_for_next": "object (optional)"
}
```
---
### session_resume
Resume a paused session.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)"
}
```
**Output:**
```json
{
"success": true,
"session": {...},
"handoff_context": {
"summary": "Previous work summary",
"next_steps": ["Continue with..."],
"blockers": []
}
}
```
---
### session_replay
Get replay context for a session.
**Scopes:** `read`
**Input:**
```json
{
"session_id": "string (required)"
}
```
**Output:**
```json
{
"success": true,
"replay_context": {
"session_id": "ses_abc123",
"progress_summary": {...},
"last_checkpoint": {...},
"decisions": [...],
"errors": [...]
}
}
```
---
### session_continue
Create a new session that continues from a previous one.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"agent_type": "string (optional)"
}
```
---
### session_artifact
Add an artifact (file) to a session.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"path": "string (required)",
"action": "string (required: created|modified|deleted)",
"metadata": "object (optional)"
}
```
---
### session_list
List sessions with optional filtering.
**Scopes:** `read`
**Input:**
```json
{
"plan_slug": "string (optional)",
"status": "string (optional)",
"limit": "integer (optional)"
}
```
## State Tools
### state_set
Set a workspace state value.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"key": "string (required)",
"value": "any (required)",
"category": "string (optional)"
}
```
---
### state_get
Get a workspace state value.
**Scopes:** `read`
**Input:**
```json
{
"plan_slug": "string (required)",
"key": "string (required)"
}
```
---
### state_list
List all state for a plan.
**Scopes:** `read`
**Input:**
```json
{
"plan_slug": "string (required)",
"category": "string (optional)"
}
```
## Task Tools
### task_update
Update a task within a phase.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)",
"task_identifier": "string|integer (required)",
"status": "string (optional: pending|completed)",
"notes": "string (optional)"
}
```
---
### task_toggle
Toggle a task's completion status.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)",
"task_identifier": "string|integer (required)"
}
```
## Template Tools
### template_list
List available plan templates.
**Scopes:** `read`
**Output:**
```json
{
"success": true,
"templates": [
{
"slug": "feature-development",
"name": "Feature Development",
"description": "Standard feature workflow",
"phases_count": 5,
"variables": [
{
"name": "FEATURE_NAME",
"required": true
}
]
}
]
}
```
---
### template_preview
Preview a template with variable substitution.
**Scopes:** `read`
**Input:**
```json
{
"slug": "string (required)",
"variables": {
"FEATURE_NAME": "Authentication"
}
}
```
---
### template_create_plan
Create a plan from a template.
**Scopes:** `write`
**Input:**
```json
{
"template_slug": "string (required)",
"variables": "object (required)",
"title": "string (optional, overrides template)",
"activate": "boolean (optional, default false)"
}
```
## Content Tools
### content_generate
Generate content using AI.
**Scopes:** `write`
**Input:**
```json
{
"prompt": "string (required)",
"provider": "string (optional: claude|gemini|openai)",
"config": {
"temperature": 0.7,
"max_tokens": 4000
}
}
```
---
### content_batch_generate
Generate content for a batch specification.
**Scopes:** `write`
**Input:**
```json
{
"batch_id": "string (required)",
"provider": "string (optional)",
"dry_run": "boolean (optional)"
}
```
---
### content_brief_create
Create a content brief for later generation.
**Scopes:** `write`
---
### content_brief_get
Get a content brief.
**Scopes:** `read`
---
### content_brief_list
List content briefs.
**Scopes:** `read`
---
### content_status
Get batch generation status.
**Scopes:** `read`
---
### content_usage_stats
Get AI usage statistics.
**Scopes:** `read`
---
### content_from_plan
Generate content based on plan context.
**Scopes:** `write`
## Error Responses
All tools return errors in this format:
```json
{
"error": "Error message",
"code": "error_code"
}
```
Common error codes:
- `validation_error` - Invalid input
- `not_found` - Resource not found
- `permission_denied` - Insufficient permissions
- `rate_limited` - Rate limit exceeded
- `service_unavailable` - Circuit breaker open
## Circuit Breaker
Tools use circuit breaker protection for database calls. When the circuit opens:
```json
{
"error": "Agentic service temporarily unavailable",
"code": "service_unavailable"
}
```
The circuit resets after successful health checks.

View file

@ -0,0 +1,670 @@
---
title: MCP Tools Reference
description: Complete reference for core-agentic MCP tools
updated: 2026-01-29
---
# MCP Tools Reference
This document provides a complete reference for all MCP tools in the `core-agentic` package.
## Overview
Tools are organised into categories:
| Category | Description | Tools Count |
|----------|-------------|-------------|
| plan | Work plan management | 5 |
| phase | Phase operations | 3 |
| session | Session tracking | 8 |
| state | Persistent state | 3 |
| task | Task completion | 2 |
| template | Plan templates | 3 |
| content | Content generation | 6 |
## Plan Tools
### plan_create
Create a new work plan with phases and tasks.
**Scopes:** `write`
**Input:**
```json
{
"title": "string (required)",
"slug": "string (optional, auto-generated)",
"description": "string (optional)",
"context": "object (optional)",
"phases": [
{
"name": "string",
"description": "string",
"tasks": ["string"]
}
]
}
```
**Output:**
```json
{
"success": true,
"plan": {
"slug": "my-plan-abc123",
"title": "My Plan",
"status": "draft",
"phases": 3
}
}
```
**Dependencies:** workspace_id in context
---
### plan_get
Get a plan by slug with full details.
**Scopes:** `read`
**Input:**
```json
{
"slug": "string (required)"
}
```
**Output:**
```json
{
"success": true,
"plan": {
"slug": "my-plan",
"title": "My Plan",
"status": "active",
"progress": {
"total": 5,
"completed": 2,
"percentage": 40
},
"phases": [...]
}
}
```
---
### plan_list
List plans with optional filtering.
**Scopes:** `read`
**Input:**
```json
{
"status": "string (optional: draft|active|completed|archived)",
"limit": "integer (optional, default 20)"
}
```
**Output:**
```json
{
"success": true,
"plans": [
{
"slug": "plan-1",
"title": "Plan One",
"status": "active"
}
],
"count": 1
}
```
---
### plan_update_status
Update a plan's status.
**Scopes:** `write`
**Input:**
```json
{
"slug": "string (required)",
"status": "string (required: draft|active|completed|archived)"
}
```
---
### plan_archive
Archive a plan with optional reason.
**Scopes:** `write`
**Input:**
```json
{
"slug": "string (required)",
"reason": "string (optional)"
}
```
## Phase Tools
### phase_get
Get phase details by plan slug and phase order.
**Scopes:** `read`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)"
}
```
---
### phase_update_status
Update a phase's status.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)",
"status": "string (required: pending|in_progress|completed|blocked|skipped)",
"reason": "string (optional, for blocked/skipped)"
}
```
---
### phase_add_checkpoint
Add a checkpoint note to a phase.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)",
"note": "string (required)",
"context": "object (optional)"
}
```
## Session Tools
### session_start
Start a new agent session.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (optional)",
"agent_type": "string (required: opus|sonnet|haiku)",
"context": "object (optional)"
}
```
**Output:**
```json
{
"success": true,
"session": {
"session_id": "ses_abc123xyz",
"agent_type": "opus",
"plan": "my-plan",
"status": "active"
}
}
```
---
### session_end
End a session with status and summary.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"status": "string (required: completed|failed)",
"summary": "string (optional)"
}
```
---
### session_log
Add a work log entry to an active session.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"message": "string (required)",
"type": "string (optional: info|warning|error|success|checkpoint)",
"data": "object (optional)"
}
```
---
### session_handoff
Prepare session for handoff to another agent.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"summary": "string (required)",
"next_steps": ["string"],
"blockers": ["string"],
"context_for_next": "object (optional)"
}
```
---
### session_resume
Resume a paused session.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)"
}
```
**Output:**
```json
{
"success": true,
"session": {...},
"handoff_context": {
"summary": "Previous work summary",
"next_steps": ["Continue with..."],
"blockers": []
}
}
```
---
### session_replay
Get replay context for a session.
**Scopes:** `read`
**Input:**
```json
{
"session_id": "string (required)"
}
```
**Output:**
```json
{
"success": true,
"replay_context": {
"session_id": "ses_abc123",
"progress_summary": {...},
"last_checkpoint": {...},
"decisions": [...],
"errors": [...]
}
}
```
---
### session_continue
Create a new session that continues from a previous one.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"agent_type": "string (optional)"
}
```
---
### session_artifact
Add an artifact (file) to a session.
**Scopes:** `write`
**Input:**
```json
{
"session_id": "string (required)",
"path": "string (required)",
"action": "string (required: created|modified|deleted)",
"metadata": "object (optional)"
}
```
---
### session_list
List sessions with optional filtering.
**Scopes:** `read`
**Input:**
```json
{
"plan_slug": "string (optional)",
"status": "string (optional)",
"limit": "integer (optional)"
}
```
## State Tools
### state_set
Set a workspace state value.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"key": "string (required)",
"value": "any (required)",
"category": "string (optional)"
}
```
---
### state_get
Get a workspace state value.
**Scopes:** `read`
**Input:**
```json
{
"plan_slug": "string (required)",
"key": "string (required)"
}
```
---
### state_list
List all state for a plan.
**Scopes:** `read`
**Input:**
```json
{
"plan_slug": "string (required)",
"category": "string (optional)"
}
```
## Task Tools
### task_update
Update a task within a phase.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)",
"task_identifier": "string|integer (required)",
"status": "string (optional: pending|completed)",
"notes": "string (optional)"
}
```
---
### task_toggle
Toggle a task's completion status.
**Scopes:** `write`
**Input:**
```json
{
"plan_slug": "string (required)",
"phase_order": "integer (required)",
"task_identifier": "string|integer (required)"
}
```
## Template Tools
### template_list
List available plan templates.
**Scopes:** `read`
**Output:**
```json
{
"success": true,
"templates": [
{
"slug": "feature-development",
"name": "Feature Development",
"description": "Standard feature workflow",
"phases_count": 5,
"variables": [
{
"name": "FEATURE_NAME",
"required": true
}
]
}
]
}
```
---
### template_preview
Preview a template with variable substitution.
**Scopes:** `read`
**Input:**
```json
{
"slug": "string (required)",
"variables": {
"FEATURE_NAME": "Authentication"
}
}
```
---
### template_create_plan
Create a plan from a template.
**Scopes:** `write`
**Input:**
```json
{
"template_slug": "string (required)",
"variables": "object (required)",
"title": "string (optional, overrides template)",
"activate": "boolean (optional, default false)"
}
```
## Content Tools
### content_generate
Generate content using AI.
**Scopes:** `write`
**Input:**
```json
{
"prompt": "string (required)",
"provider": "string (optional: claude|gemini|openai)",
"config": {
"temperature": 0.7,
"max_tokens": 4000
}
}
```
---
### content_batch_generate
Generate content for a batch specification.
**Scopes:** `write`
**Input:**
```json
{
"batch_id": "string (required)",
"provider": "string (optional)",
"dry_run": "boolean (optional)"
}
```
---
### content_brief_create
Create a content brief for later generation.
**Scopes:** `write`
---
### content_brief_get
Get a content brief.
**Scopes:** `read`
---
### content_brief_list
List content briefs.
**Scopes:** `read`
---
### content_status
Get batch generation status.
**Scopes:** `read`
---
### content_usage_stats
Get AI usage statistics.
**Scopes:** `read`
---
### content_from_plan
Generate content based on plan context.
**Scopes:** `write`
## Error Responses
All tools return errors in this format:
```json
{
"error": "Error message",
"code": "error_code"
}
```
Common error codes:
- `validation_error` - Invalid input
- `not_found` - Resource not found
- `permission_denied` - Insufficient permissions
- `rate_limited` - Rate limit exceeded
- `service_unavailable` - Circuit breaker open
## Circuit Breaker
Tools use circuit breaker protection for database calls. When the circuit opens:
```json
{
"error": "Agentic service temporarily unavailable",
"code": "service_unavailable"
}
```
The circuit resets after successful health checks.

416
docs/php-agent/RFC.md Normal file
View file

@ -0,0 +1,416 @@
# core/php/agent RFC — Agentic Module (PHP Implementation)
> The PHP implementation of the agent system, specced from existing code.
> Implements `code/core/agent/RFC.md` contract in PHP.
> An agent should be able to build agent features from this document alone.
**Module:** `dappco.re/php/agent`
**Namespace:** `Core\Mod\Agentic\*`
**Sub-specs:** [Actions](RFC.actions.md) | [Architecture](RFC.architecture.md) | [Commands](RFC.commands.md) | [Endpoints](RFC.endpoints.md) | [MCP Tools](RFC.mcp-tools.md) | [Models](RFC.models.md) | [OpenBrain Design](RFC.openbrain-design.md) | [OpenBrain Impl](RFC.openbrain-impl.md) | [Porting Plan](RFC.porting-plan.md) | [Security](RFC.security.md) | [UI](RFC.ui.md)
---
## 1. Domain Model
| Model | Table | Purpose |
|-------|-------|---------|
| `AgentPlan` | `agent_plans` | Structured work plans with phases, soft-deleted, activity-logged |
| `AgentPhase` | `agent_phases` | Individual phase within a plan (tasks, deps, status) |
| `AgentSession` | `agent_sessions` | Agent work sessions (context, work_log, artefacts, handoff) |
| `AgentMessage` | `agent_messages` | Direct agent-to-agent messaging (chronological, not semantic) |
| `AgentApiKey` | `agent_api_keys` | External agent access keys (hashed, scoped, rate-limited) |
| `BrainMemory` | `brain_memories` | Semantic knowledge store (tags, confidence, vector-indexed) |
| `Issue` | `issues` | Bug/feature/task tracking (labels, priority, sprint) |
| `IssueComment` | `issue_comments` | Comments on issues |
| `Sprint` | `sprints` | Time-boxed iterations grouping issues |
| `Task` | `tasks` | Simple tasks (title, status, file/line ref) |
| `Prompt` | `prompts` | Reusable AI prompt templates (system + user template) |
| `PromptVersion` | `prompt_versions` | Immutable prompt snapshots |
| `PlanTemplateVersion` | `plan_template_versions` | Immutable YAML template snapshots |
| `WorkspaceState` | `workspace_states` | Key-value state per plan (typed, shared across sessions) |
---
## 2. Actions
Single-responsibility action classes in `Actions/`:
### Brain
| Action | Method | Purpose |
|--------|--------|---------|
| `ForgetKnowledge` | `execute(id)` | Delete a memory |
| `ListKnowledge` | `execute(filters)` | List memories with filtering |
| `RecallKnowledge` | `execute(query)` | Semantic search via Qdrant |
| `RememberKnowledge` | `execute(content, tags)` | Store + embed memory |
### Forge
| Action | Method | Purpose |
|--------|--------|---------|
| `AssignAgent` | `execute(issue, agent)` | Assign agent to Forge issue |
| `CreatePlanFromIssue` | `execute(issue)` | Generate plan from issue description |
| `ManagePullRequest` | `execute(pr)` | Review/merge/close PRs |
| `ReportToIssue` | `execute(issue, report)` | Post agent findings to issue |
| `ScanForWork` | `execute()` | Scan Forge repos for actionable issues |
### Issue
| Action | Method | Purpose |
|--------|--------|---------|
| `CreateIssue` | `execute(data)` | Create issue |
| `GetIssue` | `execute(id)` | Get issue by ID |
| `ListIssues` | `execute(filters)` | List with filtering |
| `UpdateIssue` | `execute(id, data)` | Update fields |
| `AddIssueComment` | `execute(id, body)` | Add comment |
| `ArchiveIssue` | `execute(id)` | Soft delete |
### Plan
| Action | Method | Purpose |
|--------|--------|---------|
| `CreatePlan` | `execute(data)` | Create plan with phases |
| `GetPlan` | `execute(id)` | Get plan by ID/slug |
| `ListPlans` | `execute(filters)` | List plans |
| `UpdatePlanStatus` | `execute(id, status)` | Update plan status |
| `ArchivePlan` | `execute(id)` | Soft delete plan |
### Phase
| Action | Method | Purpose |
|--------|--------|---------|
| `GetPhase` | `execute(id)` | Get phase details |
| `UpdatePhaseStatus` | `execute(id, status)` | Update phase status |
| `AddCheckpoint` | `execute(id, checkpoint)` | Record checkpoint |
### Session
| Action | Method | Purpose |
|--------|--------|---------|
| `StartSession` | `execute(data)` | Start agent session |
| `ContinueSession` | `execute(id, data)` | Resume session |
| `EndSession` | `execute(id, summary)` | End session with summary |
| `GetSession` | `execute(id)` | Get session details |
| `ListSessions` | `execute(filters)` | List sessions |
### Sprint
| Action | Method | Purpose |
|--------|--------|---------|
| `CreateSprint` | `execute(data)` | Create sprint |
| `GetSprint` | `execute(id)` | Get sprint |
| `ListSprints` | `execute(filters)` | List sprints |
| `UpdateSprint` | `execute(id, data)` | Update sprint |
| `ArchiveSprint` | `execute(id)` | Soft delete |
### Task
| Action | Method | Purpose |
|--------|--------|---------|
| `ToggleTask` | `execute(id)` | Toggle task completion |
| `UpdateTask` | `execute(id, data)` | Update task fields |
---
## 3. API Endpoints
Routes in `Routes/api.php`, auth via `AgentApiAuth` middleware:
### Brain (`/v1/brain/*`)
| Method | Endpoint | Action |
|--------|----------|--------|
| POST | `/v1/brain/remember` | RememberKnowledge |
| POST | `/v1/brain/recall` | RecallKnowledge |
| DELETE | `/v1/brain/forget/{id}` | ForgetKnowledge |
| GET | `/v1/brain/list` | ListKnowledge |
### Plans (`/v1/plans/*`)
| Method | Endpoint | Action |
|--------|----------|--------|
| POST | `/v1/plans` | CreatePlan |
| GET | `/v1/plans` | ListPlans |
| GET | `/v1/plans/{id}` | GetPlan |
| PATCH | `/v1/plans/{id}/status` | UpdatePlanStatus |
| DELETE | `/v1/plans/{id}` | ArchivePlan |
### Sessions (`/v1/sessions/*`)
| Method | Endpoint | Action |
|--------|----------|--------|
| POST | `/v1/sessions` | StartSession |
| GET | `/v1/sessions` | ListSessions |
| GET | `/v1/sessions/{id}` | GetSession |
| POST | `/v1/sessions/{id}/continue` | ContinueSession |
| POST | `/v1/sessions/{id}/end` | EndSession |
### Messages (`/v1/messages/*`)
| Method | Endpoint | Action |
|--------|----------|--------|
| POST | `/v1/messages/send` | AgentSend |
| GET | `/v1/messages/inbox` | AgentInbox |
| GET | `/v1/messages/conversation/{agent}` | AgentConversation |
### Issues, Sprints, Tasks, Phases — similar CRUD patterns.
### Auth (`/v1/agent/auth/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/agent/auth/provision` | ProvisionAgentKey | OAuth (Authentik) |
| DELETE | `/v1/agent/auth/revoke/{key_id}` | RevokeAgentKey | AgentApiKey |
### Fleet (`/v1/fleet/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/fleet/register` | RegisterNode | AgentApiKey |
| POST | `/v1/fleet/heartbeat` | NodeHeartbeat | AgentApiKey |
| POST | `/v1/fleet/deregister` | DeregisterNode | AgentApiKey |
| GET | `/v1/fleet/nodes` | ListNodes | AgentApiKey |
| POST | `/v1/fleet/task/assign` | AssignTask | AgentApiKey |
| POST | `/v1/fleet/task/complete` | CompleteTask | AgentApiKey |
| GET | `/v1/fleet/task/next` | GetNextTask | AgentApiKey |
### Fleet Events (SSE)
| Method | Path | Purpose | Auth |
|--------|------|---------|------|
| GET | `/v1/fleet/events` | SSE stream — pushes task assignments to connected nodes | AgentApiKey |
The SSE connection stays open. When the scheduler assigns a task to a node, it pushes a `task.assigned` event. Nodes that can't hold SSE connections fall back to polling `GET /v1/fleet/task/next`.
### Fleet Stats (`/v1/fleet/stats`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| GET | `/v1/fleet/stats` | GetFleetStats | AgentApiKey |
Returns: nodes_online, tasks_today, tasks_week, repos_touched, findings_total, compute_hours.
### Sync (`/v1/agent/sync/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/agent/sync` | PushDispatchHistory | AgentApiKey |
| GET | `/v1/agent/context` | PullFleetContext | AgentApiKey |
| GET | `/v1/agent/status` | GetAgentSyncStatus | AgentApiKey |
### Credits (`/v1/credits/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/credits/award` | AwardCredits | Internal |
| GET | `/v1/credits/balance/{agent_id}` | GetBalance | AgentApiKey |
| GET | `/v1/credits/history/{agent_id}` | GetCreditHistory | AgentApiKey |
### Subscription (`/v1/subscription/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/subscription/detect` | DetectCapabilities | AgentApiKey |
| GET | `/v1/subscription/budget/{agent_id}` | GetNodeBudget | AgentApiKey |
| PUT | `/v1/subscription/budget/{agent_id}` | UpdateBudget | AgentApiKey |
---
## 4. MCP Tools
Registered via `AgentToolRegistry` in `onMcpTools()`:
### Brain Tools
| Tool | MCP Name | Maps To |
|------|----------|---------|
| `BrainRemember` | `brain_remember` | RememberKnowledge action |
| `BrainRecall` | `brain_recall` | RecallKnowledge action |
| `BrainForget` | `brain_forget` | ForgetKnowledge action |
| `BrainList` | `brain_list` | ListKnowledge action |
### Messaging Tools
| Tool | MCP Name | Maps To |
|------|----------|---------|
| `AgentSend` | `agent_send` | POST /v1/messages/send |
| `AgentInbox` | `agent_inbox` | GET /v1/messages/inbox |
| `AgentConversation` | `agent_conversation` | GET /v1/messages/conversation |
### Plan/Session/Phase/Task/Template tools — same pattern.
---
## 5. OpenBrain
OpenBrain architecture (storage layers, schema, flow, lifecycle) is defined in `code/core/agent/RFC.md` section "OpenBrain Architecture". PHP provides the MariaDB persistence layer, Qdrant integration, and Ollama embedding via `BrainService`.
---
## 6. Provider Abstraction
```php
interface AgenticProviderInterface
{
public function generate(string $prompt, array $options = []): string;
public function stream(string $prompt, array $options = [], callable $onToken): void;
public function name(): string;
public function defaultModel(): string;
public function isAvailable(): bool;
}
```
`AgenticManager` registers providers (Claude, Gemini, OpenAI) with retry + exponential backoff.
---
## 7. Session Lifecycle
```
StartSession(plan_id, agent) -> active session with context
-> Agent works, appends to work_log
-> ContinueSession(id, work) -> resume from last state
-> EndSession(id, summary, handoff_notes) -> closed
-> session_handoff tool: {summary, next_steps, blockers, context_for_next}
-> session_replay tool: recover context from completed session
```
### Workspace State
Key-value store shared between sessions within a plan:
```php
// Agent A discovers something
WorkspaceState::set($planId, 'discovered_pattern', 'observer');
// Agent B reads it later
$pattern = WorkspaceState::get($planId, 'discovered_pattern');
```
---
## 8. API Key Security
- **Hashing**: Argon2id (upgraded from SHA-256 Jan 2026)
- **Scoping**: Permission strings (`plans:read`, `plans:write`, `sessions:write`, `brain:recall`)
- **IP restriction**: IPv4/IPv6/CIDR whitelist via `IpRestrictionService`
- **Rate limiting**: Per-key configurable limits
- **Display**: Key shown once on creation, stored hashed, prefix `ak_` for identification
---
## 9. Services
| Service | Purpose |
|---------|---------|
| `AgenticManager` | Provider registry (claude, gemini, openai) |
| `AgentSessionService` | Session lifecycle management |
| `AgentApiKeyService` | API key CRUD + hashing |
| `AgentToolRegistry` | MCP tool registration |
| `BrainService` | Qdrant + Ollama integration (embed, search, store) |
| `ClaudeService` | Anthropic API client |
| `GeminiService` | Google Gemini API client |
| `OpenAIService` | OpenAI API client |
| `ForgejoService` | Forgejo API client (issues, PRs, repos) |
| `ContentService` | AI content generation pipeline |
| `PlanTemplateService` | YAML template loading + versioning |
| `IpRestrictionService` | IP whitelist enforcement |
| `AgentDetection` | Detect agent type from request headers |
---
## 10. Console Commands
| Command | Artisan | Purpose |
|---------|---------|---------|
| `TaskCommand` | `agentic:task` | Manage tasks |
| `PlanCommand` | `agentic:plan` | Manage plans |
| `GenerateCommand` | `agentic:generate` | AI content generation |
| `PlanRetentionCommand` | `agentic:plan-cleanup` | Clean old plans (scheduled daily) |
| `BrainSeedMemoryCommand` | `brain:seed-memory` | Seed brain from files |
| `BrainIngestCommand` | `brain:ingest` | Bulk ingest into brain |
| `ScanCommand` | `agentic:scan` | Scan Forge for work (every 5 min) |
| `DispatchCommand` | `agentic:dispatch` | Dispatch agents (every 2 min) |
| `PrManageCommand` | `agentic:pr-manage` | Manage PRs (every 5 min) |
| `PrepWorkspaceCommand` | `agentic:prep-workspace` | Prepare agent workspace |
---
## 11. Admin UI (Livewire)
| Component | Route | Purpose |
|-----------|-------|---------|
| `Dashboard` | `/admin/agentic` | Overview stats |
| `Plans` | `/admin/agentic/plans` | Plan listing |
| `PlanDetail` | `/admin/agentic/plans/{id}` | Single plan view |
| `Sessions` | `/admin/agentic/sessions` | Session listing |
| `SessionDetail` | `/admin/agentic/sessions/{id}` | Single session view |
| `ApiKeys` | `/admin/agentic/api-keys` | Key management |
| `ApiKeyManager` | — | Key CRUD modal |
| `Templates` | `/admin/agentic/templates` | Template management |
| `ToolAnalytics` | `/admin/agentic/tools` | Tool usage stats |
| `ToolCalls` | `/admin/agentic/tool-calls` | Tool call log |
| `Playground` | `/admin/agentic/playground` | AI playground |
| `RequestLog` | `/admin/agentic/requests` | API request log |
---
## 12. Content Generation Pipeline
The agentic module was originally built for AI-driven content generation. This is the PHP side's primary product — the Go agent inherited dispatch/workspace/brain but content generation stays PHP.
### Pipeline
```
Product Briefs (per service)
-> Prompt Templates (system + user, versioned)
-> AI Generation (Claude/Gemini via AgenticManager)
-> Drafts (blog posts, help articles, social media)
-> Quality Refinement (scoring, rewriting)
-> Publication (CMS, social scheduler, help desk)
```
### Product Briefs
Each service has a brief (`Resources/briefs/`) that gives AI the product context.
| Brief | Product |
|-------|---------|
| `host-link.md` | LinkHost |
| `host-social.md` | SocialHost |
| `host-analytics.md` | AnalyticsHost |
| `host-trust.md` | TrustHost |
| `host-notify.md` | NotifyHost |
### Prompt Templates
Versioned prompt templates in `Resources/prompts/`:
| Category | Templates |
|----------|-----------|
| **Content** | blog-post, help-article, landing-page, social-media, quality-refinement |
| **Development** | architecture-review, code-review, debug-session, test-generation |
| **Visual** | infographic, logo-generation, social-graphics |
| **System** | dappcore-writer (brand voice) |
### Natural Progression SEO
Content changes create **future revisions** (scheduled posts with no date). When Googlebot visits a page with pending revisions, the system schedules publication 8-62 minutes later — making updates appear as natural content evolution rather than bulk changes.
### MCP Content Tools
```
content_generate — Generate content from brief + prompt template
content_batch — Batch generation across services
content_brief_create — Create new product brief
```
### SEO Schema Generation
Structured data templates for generated content:
- Article (BlogPosting, TechArticle)
- FAQ (FAQPage)
- HowTo (step-by-step guides)
---
## 13. Reference Material
| Resource | Location |
|----------|----------|
| Agent contract (cross-cutting) | `code/core/agent/RFC.md` |
| Go implementation | `code/core/go/agent/RFC.md` |
| lthn.sh platform | `project/lthn/ai/RFC.md` |
---
## Changelog
- 2026-03-29: Restructured as PHP implementation spec. OpenBrain architecture and polyglot mapping moved to `code/core/agent/RFC.md`. Added contract reference. Kept all PHP-specific detail (Eloquent, Livewire, actions, services, commands, admin UI, content pipeline).
- 2026-03-27: Initial RFC specced from existing PHP codebase. 14 models, 30+ actions, 20+ API endpoints, 12 MCP tools, 10 console commands, 12 admin UI components.

View file

@ -0,0 +1,22 @@
# core/php/agent — Models
| Model | Table | Key Fields | Relationships |
|-------|-------|------------|---------------|
| `AgentPlan` | `agent_plans` | workspace_id, slug, title, description, status, agent_type, template_version_id | hasMany Phases, Sessions; belongsTo Workspace; softDeletes; logsActivity |
| `AgentPhase` | `agent_phases` | agent_plan_id, order, name, tasks (JSON), dependencies (JSON), status, completion_criteria (JSON) | belongsTo AgentPlan |
| `AgentSession` | `agent_sessions` | workspace_id, agent_plan_id, session_id (UUID), agent_type, status, context_summary (JSON), work_log (JSON), artifacts (JSON) | belongsTo Workspace, AgentPlan |
| `AgentMessage` | `agent_messages` | workspace_id, from_agent, to_agent, subject, content, read_at | belongsTo Workspace |
| `AgentApiKey` | `agent_api_keys` | workspace_id, name, key (hashed), permissions (JSON), rate_limit, call_count, last_used_at, expires_at, revoked_at | belongsTo Workspace |
| `BrainMemory` | `brain_memories` | workspace_id (UUID), agent_id, type, content, tags (JSON), project, confidence, source | belongsTo Workspace; softDeletes |
| `Issue` | `issues` | workspace_id, sprint_id, slug, title, description, type, status, priority, labels (JSON) | belongsTo Workspace, Sprint; hasMany Comments; softDeletes; logsActivity |
| `IssueComment` | `issue_comments` | issue_id, author, body, metadata (JSON) | belongsTo Issue |
| `Sprint` | `sprints` | workspace_id, slug, title, goal, status, metadata (JSON), started_at, ended_at | belongsTo Workspace; hasMany Issues; softDeletes; logsActivity |
| `Task` | `tasks` | workspace_id, title, description, status, priority, category, file_ref, line_ref | belongsTo Workspace |
| `Prompt` | `prompts` | name, category, description, system_prompt, user_template, variables (JSON), model, model_config (JSON), is_active | hasMany Versions, ContentTasks |
| `PromptVersion` | `prompt_versions` | prompt_id, version, system_prompt, user_template, variables (JSON), created_by | belongsTo Prompt, User |
| `PlanTemplateVersion` | `plan_template_versions` | slug, version, name, content (JSON), content_hash (SHA-256) | hasMany AgentPlans |
| `WorkspaceState` | `workspace_states` | agent_plan_id, key, value (JSON), type, description | belongsTo AgentPlan |
| `FleetNode` | `fleet_nodes` | workspace_id, agent_id (unique), platform, models (JSON), capabilities (JSON), status, compute_budget (JSON: {max_daily_hours, max_weekly_cost_usd, quiet_start, quiet_end, prefer_models[], avoid_models[]}), current_task_id (nullable FK), last_heartbeat_at, registered_at | belongsTo Workspace; belongsTo FleetTask (current) |
| `FleetTask` | `fleet_tasks` | workspace_id, fleet_node_id, repo, branch, task, template, agent_model, status, result (JSON), findings (JSON), changes (JSON: files_changed, insertions, deletions), report (JSON), started_at, completed_at | belongsTo Workspace, FleetNode |
| `CreditEntry` | `credit_entries` | workspace_id, fleet_node_id, task_type, amount, balance_after, description | belongsTo Workspace, FleetNode |
| `SyncRecord` | `sync_records` | fleet_node_id, direction (push/pull), payload_size, items_count, synced_at | belongsTo FleetNode |

View file

@ -0,0 +1,213 @@
# OpenBrain Design
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Shared vector-indexed knowledge store that all agents (Virgil, Charon, Darbs, LEM) read/write through MCP, building singular state across sessions.
**Architecture:** MariaDB for relational metadata + Qdrant for vector embeddings. Four MCP tools in php-agentic. Go bridge in go-ai for CLI agents. Ollama for embedding generation.
**Repos:** `dappco.re/php/agent` (primary), `dappco.re/go/ai` (bridge)
---
## Problem
Agent knowledge is scattered:
- Virgil's `MEMORY.md` files in `~/.claude/projects/*/memory/` — file-based, single-agent, no semantic search
- Plans in `docs/plans/` across repos — forgotten after completion
- Session handoff notes in `agent_sessions.handoff_notes` — JSON blobs, not searchable
- Research findings lost when context windows compress
When Charon discovers a scoring calibration bug, Virgil only knows about it if explicitly told. There's no shared knowledge graph.
## Concept
**OpenBrain** — "Open" means open protocol (MCP), not open source. All agents on the platform access the same knowledge graph via `brain_*` MCP tools. Data is stored *for agents* — structured for near-native context transfer between sessions and models.
## Data Model
### `brain_memories` table (MariaDB)
| Column | Type | Purpose |
|--------|------|---------|
| `id` | UUID | Primary key, also Qdrant point ID |
| `workspace_id` | FK | Multi-tenant isolation |
| `agent_id` | string | Who wrote it (virgil, charon, darbs, lem) |
| `type` | enum | `decision`, `observation`, `convention`, `research`, `plan`, `bug`, `architecture` |
| `content` | text | The knowledge (markdown) |
| `tags` | JSON | Topic tags for filtering |
| `project` | string nullable | Repo/project scope (null = cross-project) |
| `confidence` | float | 0.01.0, how certain the agent is |
| `supersedes_id` | UUID nullable | FK to older memory this replaces |
| `expires_at` | timestamp nullable | TTL for session-scoped context |
| `deleted_at` | timestamp nullable | Soft delete |
| `created_at` | timestamp | |
| `updated_at` | timestamp | |
### `openbrain` Qdrant collection
- **Vector dimension:** 768 (nomic-embed-text via Ollama)
- **Distance metric:** Cosine
- **Point ID:** MariaDB UUID
- **Payload:** `workspace_id`, `agent_id`, `type`, `tags`, `project`, `confidence`, `created_at` (for filtered search)
## MCP Tools
### `brain_remember` — Store a memory
```json
{
"content": "LEM emotional_register was blind to negative emotions. Fixed by adding 8 weighted pattern groups.",
"type": "bug",
"tags": ["scoring", "emotional-register", "lem"],
"project": "eaas",
"confidence": 0.95,
"supersedes": "uuid-of-outdated-memory"
}
```
Agent ID injected from MCP session context. Returns the new memory UUID.
**Pipeline:**
1. Validate input
2. Embed content via Ollama (`POST /api/embeddings`, model: `nomic-embed-text`)
3. Insert into MariaDB
4. Upsert into Qdrant with payload metadata
5. If `supersedes` set, soft-delete the old memory and remove from Qdrant
### `brain_recall` — Semantic search
```json
{
"query": "How does verdict classification work?",
"top_k": 5,
"filter": {
"project": "eaas",
"type": ["decision", "architecture"],
"min_confidence": 0.5
}
}
```
**Pipeline:**
1. Embed query via Ollama
2. Search Qdrant with vector + payload filters
3. Get top-K point IDs with similarity scores
4. Hydrate from MariaDB (content, tags, supersedes chain)
5. Return ranked results with scores
Only returns latest version of superseded memories (includes `supersedes_count` so agent knows history exists).
### `brain_forget` — Soft-delete or supersede
```json
{
"id": "uuid",
"reason": "Superseded by new calibration approach"
}
```
Sets `deleted_at` in MariaDB, removes point from Qdrant. Keeps audit trail.
### `brain_list` — Browse (no vectors)
```json
{
"project": "eaas",
"type": "decision",
"agent_id": "charon",
"limit": 20
}
```
Pure MariaDB query. For browsing, auditing, bulk export. No embedding needed.
## Architecture
### PHP side (`php-agentic`)
```
Mcp/Tools/Agent/Brain/
├── BrainRemember.php
├── BrainRecall.php
├── BrainForget.php
└── BrainList.php
Services/
└── BrainService.php # Ollama embeddings + Qdrant client + MariaDB CRUD
Models/
└── BrainMemory.php # Eloquent model
Migrations/
└── XXXX_create_brain_memories_table.php
```
`BrainService` handles:
- Ollama HTTP calls for embeddings
- Qdrant REST API (upsert, search, delete points)
- MariaDB CRUD via Eloquent
- Supersession chain management
### Go side (`go-ai`)
Thin bridge tools in the MCP server that proxy `brain_*` calls to Laravel via the existing WebSocket bridge. Same pattern as `ide_chat_send` / `ide_session_create`.
### Data flow
```
Agent (any Claude)
↓ MCP tool call
Go MCP server (local, macOS/Linux)
↓ WebSocket bridge
Laravel php-agentic (lthn.sh, de1)
↓ ↓
MariaDB Qdrant
(relational) (vectors)
Ollama (embeddings)
```
PHP-native agents skip the Go bridge — call `BrainService` directly.
### Infrastructure
- **Qdrant:** New container on de1. Shared between OpenBrain and EaaS scoring (different collections).
- **Ollama:** Existing instance. `nomic-embed-text` model for 768d embeddings. CPU is fine for the volume (~10K memories).
- **MariaDB:** Existing instance on de1. New table in the agentic database.
## Integration
### Plans → Brain
On plan completion, agents can extract key decisions/findings and `brain_remember` them. Optional — agents decide what's worth persisting. The plan itself stays in `agent_plans`; lessons learned go to the brain.
### Sessions → Brain
Handoff notes (summary, next_steps, blockers) can auto-persist as memories with `type: observation` and optional TTL. Agents can also manually remember during a session.
### MEMORY.md migration
Seed data: collect all `MEMORY.md` files from `~/.claude/projects/*/memory/` across worktrees. Parse into individual memories, embed, and load into OpenBrain. After migration, `brain_recall` replaces file-based memory.
### EaaS
Same Qdrant instance, different collection (`eaas_scoring` vs `openbrain`). Shared infrastructure, separate concerns.
### LEM
LEM models query the brain for project context during training data curation or benchmark analysis. Same MCP tools, different agent ID.
## What this replaces
- Virgil's `MEMORY.md` files (file-based, single-agent, no search)
- Scattered `docs/plans/` findings that get forgotten
- Manual "Charon found X" cross-agent handoffs
- Session-scoped knowledge that dies with context compression
## What this enables
- Any Claude picks up where another left off — semantically
- Decisions surface when related code is touched
- Knowledge graph grows with every session across all agents
- Near-native context transfer between models and sessions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,313 @@
# Agentic Task System - Porting Plan
MCP-powered workspace for persistent work plans that survive context limits and enable multi-agent collaboration.
## Why this exists
- **Context persistence** - Work plans persist across Claude sessions, surviving context window limits
- **Multi-agent collaboration** - Handoff support between different agents (Opus, Sonnet, Haiku)
- **Checkpoint verification** - Phase gates ensure work is complete before progressing
- **Workspace state** - Shared key-value storage for agents to communicate findings
## Source Location
```
/Users/snider/Code/lab/upstream/
├── app/Models/
│ ├── AgentPlan.php (6.1KB, ~200 lines)
│ ├── AgentPhase.php (7.9KB, ~260 lines)
│ ├── AgentSession.php (7.5KB, ~250 lines)
│ └── WorkspaceState.php (2.1KB, ~70 lines)
├── app/Console/Commands/
│ ├── McpAgentServerCommand.php (42KB, ~1200 lines)
│ ├── PlanCreateCommand.php (8.5KB)
│ ├── PlanListCommand.php (1.8KB)
│ ├── PlanShowCommand.php (4.0KB)
│ ├── PlanStatusCommand.php (3.7KB)
│ ├── PlanCheckCommand.php (5.7KB)
│ └── PlanPhaseCommand.php (5.8KB)
└── database/migrations/
└── 2025_12_31_000001_create_agent_tables.php
```
## Target Location
```
/Users/snider/Code/lab/dappco.re/
├── app/Models/Agent/ # New subdirectory
│ ├── AgentPlan.php
│ ├── AgentPhase.php
│ ├── AgentSession.php
│ └── WorkspaceState.php
├── app/Console/Commands/Agent/ # New subdirectory
│ ├── McpAgentServerCommand.php
│ ├── PlanCreateCommand.php
│ ├── PlanListCommand.php
│ ├── PlanShowCommand.php
│ ├── PlanStatusCommand.php
│ ├── PlanCheckCommand.php
│ └── PlanPhaseCommand.php
├── database/migrations/
│ └── 2025_12_31_100000_create_agent_tables.php
└── tests/Feature/Agent/ # New subdirectory
├── AgentPlanTest.php
├── AgentPhaseTest.php
└── PlanCommandsTest.php
```
---
## Phase 1: Database Migration
Create the migration file with all four tables.
### Tasks
- [ ] Create migration `2025_12_31_100000_create_agent_tables.php`
- [ ] Tables: `agent_plans`, `agent_phases`, `agent_sessions`, `workspace_states`
- [ ] Run migration and verify schema
### Source File
Copy from: `upstream/database/migrations/2025_12_31_000001_create_agent_tables.php`
### Schema Summary
| Table | Purpose | Key Columns |
|-------|---------|-------------|
| `agent_plans` | Work plans with phases | slug, title, status, current_phase |
| `agent_phases` | Individual phases | order, name, tasks (JSON), status, dependencies |
| `agent_sessions` | Agent work sessions | session_id, agent_type, work_log, handoff_notes |
| `workspace_states` | Shared key-value state | key, value (JSON), type |
---
## Phase 2: Eloquent Models
Port all four models with namespace adjustment.
### Tasks
- [ ] Create `app/Models/Agent/` directory
- [ ] Port `AgentPlan.php` - update namespace to `App\Models\Agent`
- [ ] Port `AgentPhase.php` - update namespace and relationships
- [ ] Port `AgentSession.php` - update namespace
- [ ] Port `WorkspaceState.php` - update namespace
### Namespace Changes
```php
// From (upstream)
namespace App\Models;
// To (dappco.re)
namespace App\Models\Agent;
```
### Relationship Updates
Update all `use` statements:
```php
use Mod\Agentic\Models\AgentPlan;
use Mod\Agentic\Models\AgentPhase;
use Mod\Agentic\Models\AgentSession;
use Mod\Agentic\Models\WorkspaceState;
```
### Key Methods to Verify
**AgentPlan:**
- `getCurrentPhase()` - proper orWhere scoping with closure
- `generateSlug()` - race-condition safe unique slug generation
- `checkAllPhasesComplete()` - completion verification
**AgentPhase:**
- `complete()` - wrapped in DB::transaction
- `canStart()` - dependency checking
- `isPending()`, `isCompleted()`, `isBlocked()`
---
## Phase 3: CLI Commands
Port all plan management commands.
### Tasks
- [ ] Create `app/Console/Commands/Agent/` directory
- [ ] Port `PlanCreateCommand.php` - markdown import support
- [ ] Port `PlanListCommand.php` - list all plans with stats
- [ ] Port `PlanShowCommand.php` - detailed plan view
- [ ] Port `PlanStatusCommand.php` - status management
- [ ] Port `PlanCheckCommand.php` - checkpoint verification
- [ ] Port `PlanPhaseCommand.php` - phase management
### Namespace Changes
```php
// From
namespace App\Console\Commands;
// To
namespace App\Console\Commands\Agent;
```
### Command Signatures
| Command | Signature | Purpose |
|---------|-----------|---------|
| `plan:create` | `plan:create {slug} {--title=} {--import=} {--activate}` | Create new plan |
| `plan:list` | `plan:list {--status=}` | List all plans |
| `plan:show` | `plan:show {slug} {--markdown}` | Show plan details |
| `plan:status` | `plan:status {slug} {--set=}` | Get/set plan status |
| `plan:check` | `plan:check {slug} {phase?}` | Verify phase completion |
| `plan:phase` | `plan:phase {slug} {phase} {--status=} {--add-task=} {--complete-task=}` | Manage phases |
---
## Phase 4: MCP Agent Server
Port the MCP server command with all tools and resources.
### Tasks
- [ ] Port `McpAgentServerCommand.php` (~1200 lines)
- [ ] Update all model imports to use `Mod\Agentic\Models\*`
- [ ] Register command in `Kernel.php` or auto-discovery
- [ ] Test JSON-RPC protocol over stdio
### MCP Tools (18 total)
| Tool | Purpose |
|------|---------|
| `plan_create` | Create new plan with phases |
| `plan_get` | Get plan by slug with all phases |
| `plan_list` | List plans (optionally filtered) |
| `plan_update` | Update plan status/metadata |
| `phase_update` | Update phase status |
| `phase_check` | **Checkpoint** - verify phase completion |
| `task_add` | Add task to a phase |
| `task_complete` | Mark task done |
| `session_start` | Begin agent session |
| `session_log` | Log action to session |
| `session_artifact` | Log file artifact |
| `session_handoff` | Prepare for agent handoff |
| `session_resume` | Resume from previous session |
| `session_complete` | Mark session completed |
| `state_set` | Store workspace state |
| `state_get` | Retrieve workspace state |
| `state_list` | List all state keys |
| `state_delete` | Delete state key |
### MCP Resources (5 total)
| Resource URI | Purpose |
|--------------|---------|
| `core://plans` | List of all work plans |
| `core://plans/{slug}` | Full plan as markdown |
| `core://plans/{slug}/phase/{n}` | Phase tasks as checklist |
| `core://state/{plan}/{key}` | Specific state value |
| `core://sessions/{id}` | Session handoff context |
---
## Phase 5: Tests ✅
Port and adapt tests for dappco.re conventions.
### Tasks
- [x] Create `app/Mod/Agentic/Tests/Feature/` directory
- [x] Create `AgentPlanTest.php` with factory support
- [x] Create `AgentPhaseTest.php` with factory support
- [x] Create `AgentSessionTest.php` with factory support
- [x] Create model factories (`AgentPlanFactory`, `AgentPhaseFactory`, `AgentSessionFactory`)
- [x] Run full test suite - 67 tests passing
### Test Coverage
- Model CRUD operations
- Relationship integrity
- Status transitions
- Phase dependency checking
- Command input/output
- MCP protocol compliance (optional E2E)
---
## Phase 6: Documentation and Integration
Finalise integration with dappco.re.
### Tasks
- [ ] Add MCP server config to `mcp.json` example
- [ ] Update `CLAUDE.md` with agentic task commands
- [ ] Create feature documentation following `_TEMPLATE.md`
- [ ] Add to route/command discovery if needed
### MCP Configuration
```json
{
"mcpServers": {
"core-agent": {
"command": "php",
"args": ["artisan", "mcp:agent-server"],
"cwd": "/Users/snider/Code/lab/dappco.re"
}
}
}
```
---
## Verification Checklist
After each phase, verify:
- [ ] No syntax errors (`php artisan list` works)
- [ ] Migrations run cleanly
- [ ] Models can be instantiated
- [ ] Commands appear in `php artisan list`
- [ ] Tests pass (`php artisan test --filter=Agent`)
---
## Files to Copy (Summary)
| Source | Target | Changes Required |
|--------|--------|------------------|
| `upstream/database/migrations/2025_12_31_000001_create_agent_tables.php` | `dappco.re/database/migrations/2025_12_31_100000_create_agent_tables.php` | Rename only |
| `upstream/app/Models/AgentPlan.php` | `dappco.re/app/Models/Agent/AgentPlan.php` | Namespace |
| `upstream/app/Models/AgentPhase.php` | `dappco.re/app/Models/Agent/AgentPhase.php` | Namespace |
| `upstream/app/Models/AgentSession.php` | `dappco.re/app/Models/Agent/AgentSession.php` | Namespace |
| `upstream/app/Models/WorkspaceState.php` | `dappco.re/app/Models/Agent/WorkspaceState.php` | Namespace |
| `upstream/app/Console/Commands/McpAgentServerCommand.php` | `dappco.re/app/Console/Commands/Agent/McpAgentServerCommand.php` | Namespace + imports |
| `upstream/app/Console/Commands/Plan*.php` (6 files) | `dappco.re/app/Console/Commands/Agent/Plan*.php` | Namespace + imports |
| `upstream/tests/Feature/Agent*.php` | `dappco.re/tests/Feature/Agent/*.php` | Namespace |
| `upstream/tests/Feature/PlanCommandsTest.php` | `dappco.re/tests/Feature/Agent/PlanCommandsTest.php` | Namespace |
---
## Estimated Effort
| Phase | Complexity | Notes |
|-------|------------|-------|
| 1. Migration | Low | Direct copy |
| 2. Models | Low | Namespace changes only |
| 3. CLI Commands | Medium | 7 files, namespace + import updates |
| 4. MCP Server | Medium | Large file, many import updates |
| 5. Tests | Low | Namespace changes |
| 6. Documentation | Low | Config and docs |
---
## Related Services
- `ContentProcessingService` - May benefit from agent tracking
- `EntitlementService` - No direct relation
- Existing `Task` model - Different purpose (simple tasks vs agent plans)
See also: `/Users/snider/Code/lab/upstream/CLAUDE.md` for original implementation details.

View file

@ -0,0 +1,279 @@
---
title: Security
description: Security considerations and audit notes for core-agentic
updated: 2026-01-29
---
# Security Considerations
This document outlines security considerations, known issues, and recommendations for the `core-agentic` package.
## Authentication
### API Key Security
**Current Implementation:**
- Keys generated with `ak_` prefix + 32 random characters
- Stored as SHA-256 hash (no salt)
- Key only visible once at creation time
- Supports expiration dates
- Supports revocation
**Known Issues:**
1. **No salt in hash (SEC-001)**
- Risk: Rainbow table attacks possible against common key formats
- Mitigation: Keys are high-entropy (32 random chars), reducing practical risk
- Recommendation: Migrate to Argon2id with salt
2. **Key prefix visible in hash display**
- The `getMaskedKey()` method shows first 6 chars of the hash, not the original key
- This is safe but potentially confusing for users
**Recommendations:**
- Consider key rotation reminders
- Add key compromise detection (unusual usage patterns)
- Implement key versioning for smooth rotation
### IP Whitelisting
**Implementation:**
- Per-key IP restriction toggle
- Supports IPv4 and IPv6
- Supports CIDR notation
- Logged when requests blocked
**Validation:**
- Uses `filter_var()` with `FILTER_VALIDATE_IP`
- CIDR prefix validated against IP version limits (0-32 for IPv4, 0-128 for IPv6)
- Normalises IPs for consistent comparison
**Edge Cases Handled:**
- Empty whitelist with restrictions enabled = deny all
- Invalid IPs/CIDRs rejected during configuration
- IP version mismatch (IPv4 vs IPv6) handled correctly
## Authorisation
### Multi-Tenancy
**Workspace Scoping:**
- All models use `BelongsToWorkspace` trait
- Queries automatically scoped to current workspace context
- Missing workspace throws `MissingWorkspaceContextException`
**Known Issues:**
1. **StateSet tool lacks workspace validation (SEC-003)**
- Risk: Plan lookup by slug without workspace constraint
- Impact: Could allow cross-tenant state manipulation if slugs collide
- Fix: Add workspace_id check to plan query
2. **Some tools have soft dependency on workspace**
- SessionStart marks workspace as optional if plan_slug provided
- Could theoretically allow workspace inference attacks
### Permission Model
**Scopes:**
- `plans:read` - List and view plans
- `plans:write` - Create, update, archive plans
- `phases.write` - Update phase status, manage tasks
- `sessions.read` - List and view sessions
- `sessions:write` - Start, update, complete sessions
- `tools.read` - View tool analytics
- `templates.read` - List and view templates
- `templates.instantiate` - Create plans from templates
**Tool Scope Enforcement:**
- Each tool declares required scopes
- `AgentToolRegistry::execute()` validates scopes before execution
- Missing scope throws `RuntimeException`
## Rate Limiting
### Current Implementation
**Global Rate Limiting:**
- ForAgentsController: 60 requests/minute per IP
- Configured via `RateLimiter::for('agentic-api')`
**Per-Key Rate Limiting:**
- Configurable per API key (default: 100/minute)
- Uses cache-based counter with 60-second TTL
- Atomic increment via `Cache::add()` + `Cache::increment()`
**Known Issues:**
1. **No per-tool rate limiting (SEC-004)**
- Risk: Single key can call expensive tools unlimited times
- Impact: Resource exhaustion, cost overrun
- Fix: Add tool-specific rate limits
2. **Rate limit counter not distributed**
- Multiple app servers may have separate counters
- Fix: Ensure Redis cache driver in production
### Response Headers
Rate limit status exposed via headers:
- `X-RateLimit-Limit` - Maximum requests allowed
- `X-RateLimit-Remaining` - Requests remaining in window
- `X-RateLimit-Reset` - Seconds until reset
- `Retry-After` - When rate limited
## Input Validation
### MCP Tool Inputs
**Validation Helpers:**
- `requireString()` - Type + optional length validation
- `requireInt()` - Type + optional min/max validation
- `requireEnum()` - Value from allowed set
- `requireArray()` - Type validation
**Known Issues:**
1. **Template variable injection (VAL-001)**
- JSON escaping added but character validation missing
- Risk: Specially crafted variables could affect template behaviour
- Recommendation: Add explicit character whitelist
2. **SQL orderByRaw pattern (SEC-002)**
- TaskCommand uses raw SQL for FIELD() ordering
- Currently safe (hardcoded values) but fragile pattern
- Recommendation: Use parameterised approach
### Content Validation
ContentService validates generated content:
- Minimum word count (600 words)
- UK English spelling checks
- Banned word detection
- Structure validation (headings required)
## Data Protection
### Sensitive Data Handling
**API Keys:**
- Plaintext only available once (at creation)
- Hash stored, never logged
- Excluded from model serialisation via `$hidden`
**Session Data:**
- Work logs may contain sensitive context
- Artifacts track file paths (not contents)
- Context summaries could contain user data
**Recommendations:**
- Add data retention policies for sessions
- Consider encrypting context_summary field
- Audit work_log for sensitive data patterns
### Logging
**Current Logging:**
- IP restriction blocks logged with key metadata
- No API key plaintext ever logged
- No sensitive context logged
**Recommendations:**
- Add audit logging for permission changes
- Log key creation/revocation events
- Consider structured logging for SIEM integration
## Transport Security
**Requirements:**
- All endpoints should be HTTPS-only
- MCP portal at mcp.dappco.re
- API endpoints under /api/agent/*
**Headers Set:**
- `X-Client-IP` - For debugging/audit
- Rate limit headers
**Recommendations:**
- Add HSTS headers
- Consider mTLS for high-security deployments
## Dependency Security
### External API Calls
AI provider services make external API calls:
- Anthropic API (Claude)
- Google AI API (Gemini)
- OpenAI API
**Security Measures:**
- API keys from environment variables only
- HTTPS connections
- 300-second timeout
- Retry with exponential backoff
**Recommendations:**
- Consider API key vault integration
- Add certificate pinning for provider endpoints
- Monitor for API key exposure in responses
### Internal Dependencies
The package depends on:
- `dappcore/core` - Event system
- `dappcore/core-tenant` - Workspace scoping
- `dappcore/core-mcp` - MCP infrastructure
All are internal packages with shared security posture.
## Audit Checklist
### Pre-Production
- [ ] All SEC-* issues in TODO.md addressed
- [ ] API key hashing upgraded to Argon2id
- [ ] StateSet workspace scoping fixed
- [ ] Per-tool rate limiting implemented
- [ ] Test coverage for auth/permission logic
### Regular Audits
- [ ] Review API key usage patterns
- [ ] Check for expired but not revoked keys
- [ ] Audit workspace scope bypass attempts
- [ ] Review rate limit effectiveness
- [ ] Check for unusual tool call patterns
### Incident Response
1. **Compromised API Key**
- Immediately revoke via `$key->revoke()`
- Check usage history in database
- Notify affected workspace owner
- Review all actions taken with key
2. **Cross-Tenant Access**
- Disable affected workspace
- Audit all data access
- Review workspace scoping logic
- Implement additional checks
## Security Contacts
For security issues:
- Create private issue in repository
- Email security@dappco.re
- Do not disclose publicly until patched
## Changelog
**2026-01-29**
- Initial security documentation
- Documented known issues SEC-001 through SEC-004
- Added audit checklist
**2026-01-21**
- Rate limiting functional (was stub)
- Admin routes now require Hades role
- ForAgentsController rate limited

16
docs/php-agent/RFC.ui.md Normal file
View file

@ -0,0 +1,16 @@
# core/php/agent — Admin UI (Livewire Components)
| Component | Class | Route | Purpose |
|-----------|-------|-------|---------|
| Dashboard | `Dashboard` | `/admin/agentic` | Agent overview (active sessions, plan stats, brain count) |
| Plans | `Plans` | `/admin/agentic/plans` | Plan listing with filters |
| Plan Detail | `PlanDetail` | `/admin/agentic/plans/{id}` | Single plan with phases, tasks, timeline |
| Sessions | `Sessions` | `/admin/agentic/sessions` | Session listing |
| Session Detail | `SessionDetail` | `/admin/agentic/sessions/{id}` | Session work log, artifacts, handoff |
| API Keys | `ApiKeys` | `/admin/agentic/api-keys` | Key listing |
| API Key Manager | `ApiKeyManager` | — | Key CRUD modal (create, revoke, permissions) |
| Templates | `Templates` | `/admin/agentic/templates` | Plan template management |
| Tool Analytics | `ToolAnalytics` | `/admin/agentic/tools` | MCP tool usage stats |
| Tool Calls | `ToolCalls` | `/admin/agentic/tool-calls` | Tool call log (debug) |
| Playground | `Playground` | `/admin/agentic/playground` | AI prompt playground |
| Request Log | `RequestLog` | `/admin/agentic/requests` | API request log |

196
docs/plugins/RFC.md Normal file
View file

@ -0,0 +1,196 @@
# core/agent/plugins RFC — Claude, Codex, Gemini Plugin Specs
> The authoritative spec for the agent plugin ecosystem.
> Each plugin provides IDE-specific context, skills, and agents.
---
## 1. Plugin Architecture
Each AI agent type gets a plugin directory in `code/core/agent/`:
```
core/agent/
├── claude/ # Claude Code plugin
│ ├── core/ # Core skills (dispatch, review, scan, etc.)
│ ├── devops/ # DevOps skills (workspace, PR, issue, deps)
│ └── research/ # Research skills (archaeology, papers, mining)
├── codex/ # OpenAI Codex plugin
│ ├── core/ # Core context
│ ├── api/ # API generation
│ ├── code/ # Code quality scripts
│ ├── ci/ # CI integration
│ ├── ethics/ # LEK axioms as constraints
│ ├── guardrails/ # Safety guardrails
│ ├── qa/ # QA automation
│ ├── review/ # Code review
│ ├── verify/ # Verification
│ └── ... (15+ contexts)
├── google/ # Google Gemini
│ └── gemini-cli/ # Gemini CLI integration
└── php/ # PHP module (specced in core/php/agent)
```
---
## 2. Claude Plugin
### 2.1 Core Namespace (`claude/core/`)
**Commands (slash commands):**
| Command | Purpose |
|---------|---------|
| `/dispatch` | Dispatch agent to workspace |
| `/scan` | Scan Forge for actionable issues |
| `/status` | Show workspace status |
| `/review` | Review completed workspace |
| `/review-pr` | Review a pull request |
| `/pipeline` | Run 5-agent review pipeline |
| `/code-review` | Code review staged changes |
| `/security` | Security-focused review |
| `/tests` | Verify tests pass |
| `/ready` | Quick check if work is committable |
| `/verify` | Verify work before stopping |
| `/remember` | Save to OpenBrain |
| `/recall` | Search OpenBrain |
| `/sweep` | Sweep repos with dispatch |
| `/yes` | Auto-approve mode |
**Agents (subagents):**
| Agent | Purpose |
|-------|---------|
| `agent-task-code-review` | Review code for bugs, security, conventions |
| `agent-task-code-simplifier` | Simplify code for clarity |
**Skills:**
| Skill | Purpose |
|-------|---------|
| `app-split` | Extract Website module to standalone app |
| `deploy-homelab` | Deploy to lthn.sh |
| `deploy-production` | Deploy to de1 via Ansible |
| `repo-sweep` | Dispatch agents across repos |
| `architecture-review` | Review architecture decisions |
| `security-review` | Security audit |
| `senior-dev-fix` | Fix with senior dev approach |
| `test-analysis` | Analyse test coverage |
| `orchestrate` | Multi-agent orchestration |
| `reality-check` | Verify claims against code |
### 2.2 DevOps Namespace (`claude/devops/`)
**Agents:**
| Agent | Purpose |
|-------|---------|
| `agent-task-health-check` | System health check |
| `agent-task-install-core-agent` | Build + install core-agent |
| `agent-task-repair-core-agent` | Diagnose + repair core-agent |
| `agent-task-merge-workspace` | Merge completed workspace |
| `agent-task-clean-workspaces` | Remove stale workspaces |
**Skills:**
| Skill | Purpose |
|-------|---------|
| `update-deps` | Update Go module dependencies |
| `build-prompt` | Preview dispatch prompt |
| `workspace-list` | List agent workspaces |
| `workspace-clean` | Clean workspaces |
| `pr-list` / `pr-get` / `pr-merge` | PR management |
| `issue-list` / `issue-get` / `issue-comment` | Issue management |
| `repo-list` / `repo-get` | Repository queries |
### 2.3 Research Namespace (`claude/research/`)
**Skills:**
| Skill | Purpose |
|-------|---------|
| `project-archaeology` | Deep-dive into archived projects |
| `ledger-papers` | Academic paper collection (20 categories, CryptoNote heritage) |
| `bitcointalk` | BitcoinTalk thread research |
| `mining-pools` | Mining pool research |
| `wallet-releases` | Wallet release tracking |
| `whitepaper-archive` | Whitepaper collection |
| `coinmarketcap` | Market data research |
| `github-history` | GitHub repo archaeology |
| `block-explorer` | Blockchain explorer research |
| `community-chat` | Community chat analysis |
| `cryptonote-discovery` | CryptoNote project discovery |
| `job-collector` | Job market research |
---
## 3. Codex Plugin
### 3.1 Structure
Codex uses directory-based context injection. Each directory provides:
- `AGENTS.md` — agent instructions
- `scripts/` — automation scripts
- Templates for specific task types
### 3.2 Contexts
| Context | Purpose |
|---------|---------|
| `core/` | Core framework conventions |
| `api/` | API generation (OpenAPI, PHP routes) |
| `code/` | Code quality (parser, refactor, type checker) |
| `ci/` | CI pipeline integration |
| `ethics/` | LEK axioms as hard constraints |
| `guardrails/` | Safety guardrails (blue-team posture) |
| `qa/` | QA automation |
| `review/` | Code review context |
| `verify/` | Verification steps |
| `awareness/` | Codebase awareness |
| `collect/` | Data collection |
| `coolify/` | Coolify deployment |
| `issue/` | Issue management |
| `perf/` | Performance analysis |
### 3.3 Ethics
LEK axioms enforced as hard constraints. See `project/lthn/lem/RFC.md` §2 for the 5 axioms.
Blue-team posture: prevent harm, reduce exposure, harden by default.
---
## 4. Gemini Plugin
Minimal — CLI integration via `google/gemini-cli/`. Used for batch operations and TPU-credit scoring.
---
## 5. Cross-Plugin Contract
All plugins share:
- Same MCP tool names (`brain_remember`, `agent_send`, etc.)
- Same API endpoints (`/v1/plans`, `/v1/sessions`, etc.)
- Same CODEX.md / CLAUDE.md template format
- Same conventional commit format
- Same UK English spelling
- Same LEK ethics constraints
The plugin is the agent-specific layer. The tools and API are the universal contract.
---
## 6. Reference Material
| Resource | Location |
|----------|----------|
| Claude plugin | `~/Code/core/agent/claude/` (code repo) |
| Codex plugin | `~/Code/core/agent/codex/` (code repo) |
| Gemini plugin | `~/Code/core/agent/google/` (code repo) |
| Agent RFC (polyglot) | `code/core/agent/RFC.md` |
| PHP agent RFC | `code/core/php/agent/RFC.md` |
| Go agent RFC | `code/core/go/agent/RFC.md` |
---
## Changelog
- 2026-03-27: Initial RFC speccing all three agent plugins from existing code.