feat(cli): add software-factory operator lane

This commit is contained in:
Boden Fuller
2026-04-02 15:11:54 -04:00
parent e8f8ed6b92
commit bc7e7937bb
13 changed files with 552 additions and 95 deletions
+26 -1
View File
@@ -7,7 +7,7 @@
### Every session starts where the last one left off.
Validation, memory, and lifecycle gates for coding agents — plain files, zero infrastructure, zero telemetry, and knowledge that compounds across every session.
Validation, memory, lifecycle gates, and briefing-first control for coding agents. AgentOps acts as a software-factory control plane: bounded context goes in, validated code and durable learning come out.
[Start Here](#start-here) · [Install](#install) · [See It Work](#see-it-work) · [Skills](#skills) · [CLI](#the-ao-cli) · [FAQ](#faq) · [Newcomer Guide](docs/newcomer-guide.md)
@@ -29,6 +29,15 @@ Session 1, your agent spends 2 hours debugging a timeout bug. Session 15, a new
Every skill, hook, and CLI command exists to deliver one of these three. They form a single [lifecycle contract](docs/context-lifecycle.md), not separate features.
Operationally, that means AgentOps behaves like a software factory:
- briefings and startup context prepare the work order
- RPI runs the delivery line
- validation gates accept or reject output
- the flywheel turns completed work into future advantage
See [Software Factory Surface](docs/software-factory.md) for the explicit operator lane.
| Capability | What you get |
|-----|---------------|
| Judgment validation | `/pre-mortem` challenges your plan before build; `/vibe` + `/council` validate code before commit |
@@ -100,6 +109,16 @@ When you're ready for more:
Every skill works alone. Compose them however you want. Full catalog: [Skills](#skills).
If you want the explicit operator lane instead of individual primitives:
```bash
ao factory start --goal "fix auth startup"
/rpi "fix auth startup" # or: ao rpi phased "fix auth startup"
ao codex stop
```
That path keeps briefing, runtime startup, delivery, and loop closure on one surface. See [Software Factory Surface](docs/software-factory.md).
---
## How It Works
@@ -114,6 +133,12 @@ Each phase delivers one or more of the three capabilities — judgment, learning
`/rpi` orchestrates all three phases. `/evolve` keeps running `/rpi` against `GOALS.md` so the worst fitness gap gets addressed next. The output is code + state + memory + gates.
The explicit CLI operator surface around that line is:
- `ao factory start` for briefing-first startup
- `/rpi` or `ao rpi phased` for delivery
- `ao codex stop` for explicit loop closure
| Pattern | Chain | When |
|---------|-------|------|
| **Quick fix** | `/implement` | One issue, clear scope |
+48 -72
View File
@@ -1,6 +1,26 @@
# ao - Agent Operations CLI
# ao - AgentOps Software Factory CLI
CLI for the CASS (Contextual Agent Session Search) knowledge flywheel. Automates knowledge capture, retrieval, and reinforcement learning across Claude Code sessions.
`ao` is the explicit operator surface for AgentOps when you want the repo to
behave like a software factory instead of a loose collection of agent
primitives.
The short version:
```bash
ao quick-start
ao factory start --goal "fix auth startup"
ao rpi phased "fix auth startup"
ao codex stop
```
That lane keeps four concerns explicit:
1. `ao factory start` surfaces a bounded work order by compiling a goal-time
briefing when the corpus supports it, then running Codex startup.
2. `ao rpi phased` runs the delivery line with fresh context per phase.
3. `ao rpi status` lets the operator inspect long-running factory work.
4. `ao codex stop` closes the flywheel so the session leaves behind learnings,
citations, and handoff state.
## Install
@@ -11,85 +31,41 @@ go install github.com/boshu2/agentops/cli/cmd/ao@latest
## Quick Start
```bash
# From your repo root: create `.agents/` + register hooks
ao init --hooks
# From your repo root: create .agents/, starter knowledge surfaces, and hooks
ao quick-start
# Verify installation
ao hooks test
# Start a goal with briefing-first runtime context
ao factory start --goal "fix auth startup"
# Run the delivery lane
ao rpi phased "fix auth startup"
```
That's it. Knowledge now flows automatically between sessions.
If you prefer the skill-first path, use `/rpi "fix auth startup"` after
`ao factory start`.
## What It Does
**SessionStart**: Injects relevant prior knowledge weighted by freshness and utility.
**SessionEnd**: Extracts learnings and updates the feedback loop.
## Core Commands
## Operator Surfaces
| Command | Purpose |
|---------|---------|
| `ao inject` | Inject knowledge into current session |
| `ao forge transcript` | Extract learnings from session transcripts |
| `ao feedback-loop` | Update utility scores based on outcomes |
| `ao metrics report` | View flywheel health and escape velocity |
| `ao factory start --goal "<goal>"` | Compile briefing-first startup context, then run explicit Codex start |
| `ao knowledge brief --goal "<goal>"` | Build the task-time briefing directly |
| `ao codex start` | Lower-level hookless Codex startup |
| `ao rpi phased "<goal>"` | CLI-first Discovery -> Implementation -> Validation lane |
| `ao rpi status` | Monitor long-running phased work |
| `ao codex stop` | Close the loop explicitly at session end |
## Ratchet Workflow
## Lower-Level Substrate
Track progress through the RPI (Research → Plan → Implement) workflow:
These commands remain important, but they sit below the factory lane:
```bash
ao ratchet gate plan # Check if ready for planning
ao ratchet record research # Lock research completion
ao ratchet status # View current progress
```
- `ao knowledge` for belief/playbook/briefing refresh and gap reporting
- `ao context assemble` for a five-section task briefing
- `ao lookup` and `ao search` for direct retrieval
- `ao forge transcript` and `ao flywheel close-loop` for manual lifecycle work
## Hooks Management
## Reference
```bash
ao init --hooks # Recommended: repo setup + hooks (SessionStart + Stop)
ao init --hooks --full # Optional: all 8 lifecycle events
# Lower-level (hooks only; does not create `.agents/`)
ao hooks init # Generate hooks configuration
ao hooks install # Install to Claude Code
ao hooks show # View current hooks
ao hooks test # Verify hooks work
```
## Knowledge Commands
```bash
ao lookup --query "kubernetes" # Look up knowledge about k8s
ao search "error handling" # Search knowledge base
```
## Task Integration
Sync Claude Code tasks with CASS maturity system:
```bash
ao task-sync # Sync current tasks
ao task-sync --promote # Promote completed tasks
ao task-status # View maturity distribution
```
## The Science
The flywheel equation:
```
dK/dt = I(t) - δ·K + σ·ρ·K
```
- **δ = 0.17/week** - Knowledge decay rate (Darr et al.)
- **σρ > δ/100** - Operational escape velocity for compounding knowledge
See [docs/HOOKS.md](docs/HOOKS.md) for details.
For a complete command reference, see [docs/COMMANDS.md](docs/COMMANDS.md).
## License
MIT
- [Software Factory Surface](../docs/software-factory.md)
- [Session Lifecycle](../docs/workflows/session-lifecycle.md)
- [CLI Reference](docs/COMMANDS.md)
+9 -2
View File
@@ -57,6 +57,9 @@ func executeCommand(args ...string) (string, error) {
origCodexStartLimit := codexStartLimit
origCodexStartQuery := codexStartQuery
origCodexStartNoMaintenance := codexStartNoMaintenance
origFactoryStartGoal := factoryStartGoal
origFactoryStartLimit := factoryStartLimit
origFactoryStartNoMaintenance := factoryStartNoMaintenance
origCodexStopSessionID := codexStopSessionID
origCodexStopTranscriptPath := codexStopTranscriptPath
origCodexStopAutoExtract := codexStopAutoExtract
@@ -101,6 +104,9 @@ func executeCommand(args ...string) (string, error) {
codexStartLimit = origCodexStartLimit
codexStartQuery = origCodexStartQuery
codexStartNoMaintenance = origCodexStartNoMaintenance
factoryStartGoal = origFactoryStartGoal
factoryStartLimit = origFactoryStartLimit
factoryStartNoMaintenance = origFactoryStartNoMaintenance
codexStopSessionID = origCodexStopSessionID
codexStopTranscriptPath = origCodexStopTranscriptPath
codexStopAutoExtract = origCodexStopAutoExtract
@@ -232,7 +238,7 @@ func TestCobraCommandTreeRegistration(t *testing.T) {
expectedCmds := []string{
"anti-patterns", "autodev", "badge", "batch-feedback", "completion", "config",
"constraint", "context", "codex", "contradict", "curate", "dedup",
"defrag", "demo", "doctor", "extract", "feedback", "feedback-loop",
"defrag", "demo", "doctor", "extract", "factory", "feedback", "feedback-loop",
"findings", "flywheel", "forge", "gate", "goals", "handoff", "harvest", "hooks",
"index", "init", "inject", "knowledge", "lookup", "maturity",
"memory", "metrics", "migrate", "mind", "mine", "notebook", "plans",
@@ -254,6 +260,7 @@ func TestCobraCommandTreeRegistration(t *testing.T) {
// Verify parent commands have subcommands
parentExpectations := map[string][]string{
"autodev": {"init", "validate", "show"},
"factory": {"start"},
"goals": {"validate", "measure", "drift"},
"knowledge": {"activate", "beliefs", "playbooks", "brief", "gaps"},
"ratchet": {"status", "check", "next"},
@@ -286,7 +293,7 @@ func TestCobraExpectedCmdsMatchRegistration(t *testing.T) {
expectedCmds := []string{
"anti-patterns", "autodev", "badge", "batch-feedback", "completion", "config",
"constraint", "context", "codex", "contradict", "curate", "dedup",
"defrag", "demo", "doctor", "extract", "feedback", "feedback-loop",
"defrag", "demo", "doctor", "extract", "factory", "feedback", "feedback-loop",
"findings", "flywheel", "forge", "gate", "goals", "handoff", "harvest", "hooks",
"index", "init", "inject", "knowledge", "lookup", "maturity",
"memory", "metrics", "migrate", "mind", "mine", "notebook", "plans",
+176
View File
@@ -0,0 +1,176 @@
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/spf13/cobra"
)
var (
factoryStartGoal string
factoryStartLimit int
factoryStartNoMaintenance bool
)
type factoryStartResult struct {
Workspace string `json:"workspace"`
Goal string `json:"goal,omitempty"`
Briefing string `json:"briefing,omitempty"`
BriefingWarning string `json:"briefing_warning,omitempty"`
Codex codexStartResult `json:"codex"`
Recommended []string `json:"recommended,omitempty"`
}
var factoryCmd = &cobra.Command{
Use: "factory",
Short: "Software-factory operator surface for briefing-first agent work",
Long: `Software-factory operator surface for AgentOps.
This surface keeps the operator lane explicit:
1. Compile a bounded goal-time briefing when the knowledge corpus can support it
2. Start the runtime with explicit startup context
3. Run the delivery lane through /rpi or ao rpi phased
4. Close the loop with ao codex stop and ao knowledge activate
Use lower-level commands like ao knowledge, ao codex, ao context assemble, and
ao rpi when you need substrate-level control.`,
}
var factoryStartCmd = &cobra.Command{
Use: "start",
Short: "Compile a briefing when possible, then run explicit Codex startup",
Long: `Start the software-factory operator lane for a concrete goal.
When --goal is provided, AgentOps attempts to build a goal-time briefing from
the local .agents corpus before surfacing Codex startup context. The startup
path then retrieves ranked briefings, learnings, patterns, findings, research,
and next work for the same goal so the runtime starts from a bounded work
packet instead of a broad recency dump.`,
Args: cobra.NoArgs,
RunE: runFactoryStart,
}
func init() {
factoryCmd.GroupID = "workflow"
rootCmd.AddCommand(factoryCmd)
factoryCmd.AddCommand(factoryStartCmd)
factoryStartCmd.Flags().StringVar(&factoryStartGoal, "goal", "", "Goal to brief and use as the startup query")
factoryStartCmd.Flags().IntVar(&factoryStartLimit, "limit", 3, "Maximum artifacts to surface per category during startup")
factoryStartCmd.Flags().BoolVar(&factoryStartNoMaintenance, "no-maintenance", false, "Skip safe close-loop maintenance on start")
}
func runFactoryStart(cmd *cobra.Command, args []string) error {
cwd, err := resolveProjectDir()
if err != nil {
return err
}
result, err := performFactoryStart(cwd, strings.TrimSpace(factoryStartGoal), factoryStartLimit, factoryStartNoMaintenance)
if err != nil {
return err
}
return outputFactoryStartResult(result)
}
func performFactoryStart(cwd, goal string, limit int, noMaintenance bool) (factoryStartResult, error) {
result := factoryStartResult{
Workspace: cwd,
Goal: goal,
Recommended: factoryRecommendedCommands(goal),
}
if goal != "" {
agentsRoot := filepath.Join(cwd, ".agents")
run, err := runKnowledgeNativeBuilder(cwd, agentsRoot, knowledgeBuilderInvocation{
Step: "briefing",
Implementation: knowledgeBuilderImplementationAONative,
Args: []string{"--goal", goal},
})
if err != nil {
result.BriefingWarning = err.Error()
} else {
result.Briefing = firstNonEmptyTrimmed(run.Metadata["briefing"], run.Path)
}
}
origQuery := codexStartQuery
origLimit := codexStartLimit
origNoMaintenance := codexStartNoMaintenance
codexStartQuery = goal
codexStartLimit = limit
codexStartNoMaintenance = noMaintenance
defer func() {
codexStartQuery = origQuery
codexStartLimit = origLimit
codexStartNoMaintenance = origNoMaintenance
}()
codexResult, err := performCodexStart(cwd)
if err != nil {
return factoryStartResult{}, err
}
result.Codex = codexResult
if result.Briefing == "" && len(codexResult.Briefings) > 0 {
result.Briefing = codexResult.Briefings[0].Path
}
return result, nil
}
func outputFactoryStartResult(result factoryStartResult) error {
if GetOutput() == "json" {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(result)
}
fmt.Println("Software Factory Start")
fmt.Println("======================")
fmt.Printf("Workspace: %s\n", result.Workspace)
if result.Goal != "" {
fmt.Printf("Goal: %s\n", result.Goal)
}
if result.Briefing != "" {
fmt.Printf("Briefing: %s\n", result.Briefing)
} else if result.Goal != "" {
fmt.Println("Briefing: not built")
} else {
fmt.Println("Briefing: skipped (no --goal provided)")
}
if result.BriefingWarning != "" {
fmt.Printf("Briefing note: %s\n", result.BriefingWarning)
}
fmt.Printf("Startup context: %s\n", result.Codex.StartupContextPath)
if result.Codex.MemoryPath != "" {
fmt.Printf("Memory: %s\n", result.Codex.MemoryPath)
}
fmt.Println()
fmt.Println("Factory lane:")
for _, step := range result.Recommended {
fmt.Printf(" - %s\n", step)
}
return nil
}
func factoryRecommendedCommands(goal string) []string {
if goal == "" {
return []string{
"Set a concrete goal, then run `ao factory start --goal \"your goal\"` for a briefing-first startup.",
"Run `/rpi \"your goal\"` for the skill-first delivery lane, or `ao rpi phased \"your goal\"` for CLI-first phase isolation.",
"Use `ao rpi status` to monitor long-running phased work.",
"Run `ao codex stop` when the session ends so the flywheel closes explicitly.",
}
}
quotedGoal := fmt.Sprintf("%q", goal)
return []string{
fmt.Sprintf("Run `/rpi %s` for the skill-first software-factory lane.", quotedGoal),
fmt.Sprintf("Or run `ao rpi phased %s` for CLI-first phase isolation.", quotedGoal),
"Use `ao rpi status` to monitor long-running phased work.",
"Run `ao codex stop` when the session ends so the flywheel closes explicitly.",
}
}
+112
View File
@@ -0,0 +1,112 @@
package main
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
func TestFactoryStartJSONBuildsBriefingThenRunsCodexStartup(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
t.Setenv("CODEX_THREAD_ID", "019d1bf7-58ea-79e1-9f5d-02109d930081")
t.Setenv("CODEX_INTERNAL_ORIGINATOR_OVERRIDE", "Codex Desktop")
indexPath := filepath.Join(home, ".codex", "session_index.jsonl")
if err := os.MkdirAll(filepath.Dir(indexPath), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(indexPath, []byte(`{"id":"019d1bf7-58ea-79e1-9f5d-02109d930081","thread_name":"factory startup","updated_at":"2026-03-23T12:00:00Z"}`+"\n"), 0o644); err != nil {
t.Fatal(err)
}
repo := t.TempDir()
writeKnowledgeCorpusFixtures(t, repo)
origProjectDir := testProjectDir
testProjectDir = repo
defer func() { testProjectDir = origProjectDir }()
out, err := executeCommand("factory", "start", "--json", "--no-maintenance", "--goal", "Healthy topic rollout")
if err != nil {
t.Fatalf("factory start --json: %v\noutput: %s", err, out)
}
var result factoryStartResult
if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &result); err != nil {
t.Fatalf("parse factory start json: %v\noutput: %s", err, out)
}
if result.Goal != "Healthy topic rollout" {
t.Fatalf("goal = %q, want %q", result.Goal, "Healthy topic rollout")
}
if result.Briefing == "" || !knowledgePathExists(result.Briefing) {
t.Fatalf("briefing missing: %q", result.Briefing)
}
if result.Codex.StartupContextPath == "" || !fileExists(result.Codex.StartupContextPath) {
t.Fatalf("startup context missing: %q", result.Codex.StartupContextPath)
}
if len(result.Codex.Briefings) == 0 {
t.Fatal("expected codex startup to surface the generated briefing")
}
if len(result.Recommended) == 0 {
t.Fatal("expected recommended next steps")
}
data, err := os.ReadFile(result.Codex.StartupContextPath)
if err != nil {
t.Fatalf("read startup context: %v", err)
}
content := string(data)
if !strings.Contains(content, "# Briefings") {
t.Fatalf("startup context missing briefings section:\n%s", content)
}
if !strings.Contains(content, "Healthy topic rollout") {
t.Fatalf("startup context missing goal-specific briefing:\n%s", content)
}
}
func TestFactoryStartJSONWarnsWhenBriefingCannotBeBuiltYet(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
t.Setenv("CODEX_THREAD_ID", "019d1bf7-58ea-79e1-9f5d-02109d930082")
t.Setenv("CODEX_INTERNAL_ORIGINATOR_OVERRIDE", "Codex Desktop")
indexPath := filepath.Join(home, ".codex", "session_index.jsonl")
if err := os.MkdirAll(filepath.Dir(indexPath), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(indexPath, []byte(`{"id":"019d1bf7-58ea-79e1-9f5d-02109d930082","thread_name":"factory startup no corpus","updated_at":"2026-03-23T12:00:00Z"}`+"\n"), 0o644); err != nil {
t.Fatal(err)
}
repo := t.TempDir()
if err := os.MkdirAll(filepath.Join(repo, ".agents"), 0o755); err != nil {
t.Fatal(err)
}
origProjectDir := testProjectDir
testProjectDir = repo
defer func() { testProjectDir = origProjectDir }()
out, err := executeCommand("factory", "start", "--json", "--no-maintenance", "--goal", "No topic packets yet")
if err != nil {
t.Fatalf("factory start without topic packets should still succeed: %v\noutput: %s", err, out)
}
var result factoryStartResult
if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &result); err != nil {
t.Fatalf("parse factory start json: %v\noutput: %s", err, out)
}
if result.Briefing != "" {
t.Fatalf("briefing = %q, want empty when no topic packets exist", result.Briefing)
}
if result.BriefingWarning == "" {
t.Fatal("expected briefing warning when no topic packets exist")
}
if result.Codex.StartupContextPath == "" || !fileExists(result.Codex.StartupContextPath) {
t.Fatalf("startup context missing: %q", result.Codex.StartupContextPath)
}
}
+21 -11
View File
@@ -20,7 +20,7 @@ This command:
1. Creates .agents/ directory structure
2. Optionally initializes beads (git-native issues)
3. Creates starter knowledge pack
4. Shows next steps
4. Shows the software-factory operator lane
Examples:
ao quick-start # Full setup with beads
@@ -86,6 +86,9 @@ func runQuickstart(cmd *cobra.Command, args []string) error {
fmt.Println("━━━ STEP 1: Creating .agents/ structure ━━━")
dirs := []string{
".agents/briefings",
".agents/knowledge",
".agents/playbooks",
".agents/research",
".agents/synthesis",
".agents/specs",
@@ -330,32 +333,39 @@ func showNextSteps(hasBeads bool) {
`)
if hasBeads {
fmt.Println(` 1. Create your first issue:
fmt.Println(` 1. Start the software-factory lane for a concrete goal:
$ ao factory start --goal "your first objective"
2. Create your first issue:
$ bd create "My first task"
2. Start working:
3. Start working:
$ claude
> /implement <issue-id>
> /rpi "your first objective"
3. When done, extract learnings:
> /post-mortem`)
4. When done, close the loop explicitly:
$ ao codex stop`)
} else {
fmt.Println(` 1. Start Claude in your project:
fmt.Println(` 1. Start the software-factory lane for a concrete goal:
$ ao factory start --goal "your first objective"
2. Start Claude in your project:
$ claude
2. Work normally - learnings go to .agents/
3. Work normally:
> /rpi "your first objective"
3. When ready for full power:
4. When ready for tracked execution:
$ bd init
$ bd create "My first task"`)
}
fmt.Print(`
4. Your knowledge compounds automatically via Smart Connections
5. Your knowledge compounds through briefings, RPI, and flywheel closeout
═══════════════════════════════════════════════════════════════════
"Others forget. AgentOps learns. Zero infrastructure."
"Stateful environment. Stateless agents. One explicit operator lane."
═══════════════════════════════════════════════════════════════════
`)
+6
View File
@@ -95,6 +95,9 @@ func TestQuickstart_ShowNextSteps_WithBeads(t *testing.T) {
if !strings.Contains(out, "bd") {
t.Errorf("with beads=true, expected bd reference in output:\n%s", out)
}
if !strings.Contains(out, "ao factory start") {
t.Errorf("with beads=true, expected factory lane in output:\n%s", out)
}
}
func TestQuickstart_ShowNextSteps_WithoutBeads(t *testing.T) {
@@ -102,6 +105,9 @@ func TestQuickstart_ShowNextSteps_WithoutBeads(t *testing.T) {
if out == "" {
t.Error("expected non-empty output for next steps")
}
if !strings.Contains(out, "ao factory start") {
t.Errorf("without beads, expected factory lane in output:\n%s", out)
}
}
func TestQuickstart_CreateStarterPack_CreatesPatterns(t *testing.T) {
+7 -2
View File
@@ -22,11 +22,16 @@ var (
var rootCmd = &cobra.Command{
Use: "ao",
Short: "AgentOps Knowledge Compounding CLI",
Long: `ao is the CLI for AgentOps, a knowledge compounding workflow system.
Long: `ao is the CLI for AgentOps, a software-factory control plane for repo-native agent work.
"Problem in. Value out. Intelligence compounds."
The Knowledge Flywheel:
Software Factory Lane:
ao factory start --goal "fix auth startup"
/rpi "fix auth startup" or ao rpi phased "fix auth startup"
ao codex stop
The Knowledge Flywheel underneath it:
Sessions compound via .agents/ + Smart Connections.
Others start fresh. You get smarter every session.
+29
View File
@@ -798,6 +798,35 @@ ao codex stop [flags]
---
### `ao factory`
Software-factory operator surface for AgentOps.
```
ao factory [command]
```
**Subcommands:**
#### `ao factory start`
Start the software-factory operator lane for a concrete goal.
```
ao factory start [flags]
```
**Flags:**
```
--goal string Goal to brief and use as the startup query
-h, --help help for start
--limit int Maximum artifacts to surface per category during startup (default 3)
--no-maintenance Skip safe close-loop maintenance on start
```
---
### `ao feedback-loop`
Automatically close the MemRL feedback loop by updating utilities of cited learnings.
+1
View File
@@ -15,6 +15,7 @@
## Architecture
- [How It Works](how-it-works.md) — Brownian Ratchet, Ralph Wiggum Pattern, agent backends, hooks, context windowing
- [Software Factory Surface](software-factory.md) — Explicit operator surface connecting briefings, RPI lanes, and loop closure
- [Architecture](ARCHITECTURE.md) — System design and component overview
- [Architecture Folder Index](architecture/README.md) — Architecture subdocs overview
- [Codex Hookless Lifecycle](architecture/codex-hookless-lifecycle.md) — Runtime-aware lifecycle fallback for Codex when hooks are unavailable
+5
View File
@@ -4,6 +4,11 @@
Parallel agents produce noisy output; councils filter it; ratchets lock progress so it can never regress.
Think of the mechanics below as the substrate under the software-factory
operator surface: briefings and startup context prepare the work order, RPI
phases run the delivery lane, and the flywheel closes the learning loop. See
[Software Factory Surface](software-factory.md).
## The Three Gaps
AgentOps exists because most agent tooling leaves three gaps open after prompt construction and routing are solved. The runtime mechanics described on this page are organized around closing them:
+84
View File
@@ -0,0 +1,84 @@
# Software Factory Surface
AgentOps already contains the machinery for a software factory. This document
names the operator surface explicitly so users do not have to infer it from
skills, hooks, CLI commands, and internal artifacts.
## Thesis
AgentOps is best understood as a **software-factory control plane**.
The environment carries:
- bounded briefing and context assembly
- tracked planning and scoped execution
- validation gates and ratchet checkpoints
- durable learning and loop closure
- isolated work lanes for long-running or parallel work
The workers remain replaceable. The environment carries continuity.
This follows the repo's stateful-environment/stateless-agents theory and its
own lifecycle/flywheel contracts: briefings and runtime state are the operator
surface; packets, chunks, topics, and builders are substrate.
## Operator Lane
For Codex and other explicit-runtime workflows, treat this as the canonical
lane:
```bash
ao factory start --goal "fix auth startup"
/rpi "fix auth startup"
# or: ao rpi phased "fix auth startup"
ao codex stop
```
That lane does four things:
1. `ao factory start` tries to compile a small goal-time briefing, then runs
explicit Codex startup so the session begins with bounded context.
2. `/rpi` or `ao rpi phased` runs the delivery line: discovery,
implementation, validation.
3. `ao rpi status` lets the operator inspect long-running phased work.
4. `ao codex stop` closes the flywheel explicitly so the session leaves behind
learnings, citations, and handoff state.
## Surface Map
| Layer | Purpose | Primary surfaces |
|------|---------|------------------|
| Operator | What the human or lead agent should touch first | `ao factory start`, `/rpi`, `ao rpi phased`, `ao rpi status`, `ao codex stop` |
| Briefing + runtime | Bounded startup context and thread-time state | `ao knowledge brief`, `ao codex start`, `ao codex ensure-start`, `ao context assemble` |
| Delivery line | Research, planning, execution, validation | `/discovery`, `/plan`, `/crank`, `/validation`, `/rpi` |
| Learning loop | Convert completed work into future advantage | `ao codex stop`, `ao knowledge activate`, `ao flywheel close-loop`, `/retro`, `/forge` |
| Substrate | Retrieval, provenance, packetization, and promotion machinery | `.agents/packets/`, `.agents/topics/`, `.agents/briefings/`, `.agents/findings/`, hooks, builder logic |
## Why This Surface Exists
The factory framing matters because the repo already has the hard parts:
- RPI provides the conveyor belt.
- Context packets and briefings provide bounded work orders.
- The flywheel provides durable learning and loop closure.
- Codex lifecycle commands provide explicit runtime boundaries where hooks do
not exist.
Without an explicit operator lane, users see a powerful collection of
primitives. With it, they see one product surface.
## Design Rules
- Prefer briefings over giant startup dumps.
- Keep substrate and operator surfaces distinct.
- Let external validation outrank self-report.
- Treat thin topics as discovery-only until evidence improves.
- Keep `athena` scoped to hygiene, not full operator-surface activation.
## Related Docs
- [How It Works](how-it-works.md)
- [Context Packet](context-packet.md)
- [Knowledge Flywheel](knowledge-flywheel.md)
- [Session Lifecycle](workflows/session-lifecycle.md)
- [CLI Reference](../cli/docs/COMMANDS.md)
+28 -7
View File
@@ -20,7 +20,31 @@ Just describe what you want:
| "What should I work on?" | Shows status, suggests next task |
| "Where was I?" | Shows last session, current state, blockers |
### Option 2: Codex Explicit Lifecycle (Recommended in Codex)
### Option 2: Software Factory Lane (Recommended in Codex)
```bash
# Start session with a goal-time briefing when possible
ao factory start --goal "fix auth startup"
# Run the delivery lane
/rpi "fix auth startup"
# or: ao rpi phased "fix auth startup"
# Monitor long-running phased work
ao rpi status
# End session
ao codex stop
# Inspect lifecycle + flywheel health
ao codex status
```
`ao factory start` keeps the operator lane explicit: build a bounded briefing if
the corpus can support it, run Codex startup, then move into RPI. The lower
level lifecycle commands still exist when you want direct control.
### Option 3: Lower-Level Codex Lifecycle
```bash
# Start session
@@ -32,16 +56,13 @@ ao search "topic" --cite retrieved
# End session
ao codex stop
# Inspect lifecycle + flywheel health
ao codex status
```
For normal Codex skill usage, entry skills drive the same startup path with
`ao codex ensure-start`, and closeout-owner skills drive the same closeout path
with `ao codex ensure-stop`.
### Option 3: Slash Commands (Hook-Capable Power Users)
### Option 4: Slash Commands (Hook-Capable Power Users)
```bash
# Start session
@@ -54,7 +75,7 @@ with `ao codex ensure-stop`.
/session-end
```
**Use the mode your runtime actually supports.** Claude/OpenCode can drive lifecycle via hooks. Codex uses explicit `ao codex start` / `ao codex stop` / `ao codex status`, while Codex skills automate the same boundaries via `ao codex ensure-start` / `ao codex ensure-stop`.
**Use the mode your runtime actually supports.** Claude/OpenCode can drive lifecycle via hooks. Codex users should usually prefer `ao factory start --goal "<goal>"` as the briefing-first operator surface, while `ao codex start` / `ao codex stop` / `ao codex status` remain the lower-level lifecycle primitives. Codex skills automate the same boundaries via `ao codex ensure-start` / `ao codex ensure-stop`.
---
@@ -63,7 +84,7 @@ with `ao codex ensure-stop`.
| Mode | Start | Closeout | Notes |
|------|-------|----------|-------|
| Hook-capable | Natural language, `/session-start`, or startup hooks | Natural language, `/session-end`, or session-end hooks | Best fit for Claude/OpenCode when hooks are installed |
| Codex hookless fallback | `ao codex start` or skill-driven `ao codex ensure-start` | `ao codex stop` or skill-driven `ao codex ensure-stop` | No startup/session-end hook surface under `~/.codex`; lifecycle is explicit |
| Codex hookless fallback | `ao factory start --goal "<goal>"`, `ao codex start`, or skill-driven `ao codex ensure-start` | `ao codex stop` or skill-driven `ao codex ensure-stop` | No startup/session-end hook surface under `~/.codex`; lifecycle is explicit |
| Manual fallback | `ao inject`, `ao lookup` | `ao forge transcript`, `ao flywheel close-loop` | Lowest-level portable path |
---