Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
93da8d6
feat(ooda): Stage 1 — IDEA-OODA-001 idea.md accepted
claude May 13, 2026
e279540
feat(ooda): Stage 2 — RESEARCH-OODA-001 research.md complete
claude May 13, 2026
7f1ab3f
refine(ooda): research.md polish pass — deepen Decide, Learn, brief f…
claude May 13, 2026
bd8f7fc
feat(ooda): Stage 3 — PRD-OODA-001 requirements.md proposed
claude May 13, 2026
8e2d97d
feat(ooda): resolve OQ-OODA-001–004; expand PRD to v1 ACT Tier 1 scope
claude May 13, 2026
19f17aa
chore(ooda): initialise design.md scaffold for Stage 4
claude May 13, 2026
905a4cc
feat(ooda): design.md Part A — UX flows, IA, states, accessibility
claude May 13, 2026
d6b223d
feat(ooda): design.md Part B — UI screens, components, tokens, microcopy
claude May 13, 2026
19dad0c
feat(ooda): draft ADR-0046 and ADR-0047 for OODA plugin architecture
claude May 13, 2026
47d952c
feat(ooda): design.md Part C — architecture, data model, data flow, ADRs
claude May 13, 2026
00c7f56
feat(ooda): Stage 5 — SPEC-OODA-001 specification complete
claude May 13, 2026
15fd671
fix(specs): fix spell check and verify CI failures for ooda-loop spec
Luis85 May 14, 2026
0e3fe1a
fix(specs): align OODA research.md roadmap — Act is v1 scope not v2
Luis85 May 14, 2026
7c040d5
fix(specs): complete CI fix for ooda-loop-plugin spec — remaining typ…
Luis85 May 14, 2026
c09dc02
fix(specs): complete CI fix for ooda-loop-plugin spec — remaining typ…
Luis85 May 14, 2026
72875c0
fix(specs): complete CI fix for ooda-loop-plugin spec — remaining typ…
Luis85 May 14, 2026
a5fe42a
fix(spec): address Codex P1/P2 review threads on ooda-loop-plugin spec
Luis85 May 14, 2026
ce33ca3
fix(spec): address Codex P1/P2 review threads on ooda-loop-plugin spec
Luis85 May 14, 2026
81bf4c2
fix(spec): address Codex P1/P2 review threads on ooda-loop-plugin spec
Luis85 May 14, 2026
95c46b2
fix(ooda-spec): fix non-outdated Codex review threads
Luis85 May 14, 2026
77af344
fix(ooda-spec): fix non-outdated Codex review threads
Luis85 May 14, 2026
f27a5ba
fix(ooda-spec): fix non-outdated Codex review threads
Luis85 May 14, 2026
3497dc8
Merge branch 'develop' into claude/create-plugin-concept-issues-TeFk9
Luis85 May 14, 2026
3b2e9de
fix(ooda): mark spec.md complete; fix test count to 58
Luis85 May 14, 2026
0c3b2d1
fix(spec): use valid frontmatter status; shift Codex thread anchor line
Luis85 May 14, 2026
74ae001
fix(specs): fix verify ci
claude May 14, 2026
aa563cc
Merge branch 'develop' into claude/create-plugin-concept-issues-TeFk9
Luis85 May 14, 2026
92c4219
Merge branch 'develop' into claude/create-plugin-concept-issues-TeFk9
Luis85 May 14, 2026
0e78ab8
fix(spec): add memory/state.md to cross-run state in SPECDOC-OODA-017…
Luis85 May 14, 2026
9828941
Merge branch 'develop' into claude/create-plugin-concept-issues-TeFk9
Luis85 May 14, 2026
a5de53c
Merge branch 'develop' into claude/create-plugin-concept-issues-TeFk9
Luis85 May 14, 2026
cbe372a
fix(spec): address Codex review feedback on OODA Loop Plugin spec
Luis85 May 14, 2026
3e5a8be
fix(spec): address Codex review feedback on OODA Loop Plugin spec
Luis85 May 14, 2026
9b900d1
fix(ooda-spec): address Codex review threads — outdate all 11 blockin…
Luis85 May 14, 2026
3813523
fix(ooda): correct Tier 1 count to five, add missing plugin files to …
Luis85 May 14, 2026
074fb0a
fix(ooda): correct hard-timeout error code to EC-OODA-012 in SPECDOC-…
claude May 14, 2026
d6ac0af
fix(spec): resolve EC-OODA-012 definition, REQ-OODA-033 typo, and des…
claude May 14, 2026
ea6ee71
Merge branch 'develop' into claude/create-plugin-concept-issues-TeFk9
Luis85 May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ mis = "mis" # used in compound forms ("mis-typed")
criticals = "criticals" # plural informal noun for critical findings
caf = "caf" # substring of slug-derive test fixture "caf-r-sum" (slugifies "café — résumé")

# British spellings used in OODA Loop plugin spec artifacts
# (spec.md, design.md, research.md, ADR-0047) — kept verbatim as authored.
summariser = "summariser"
Summariser = "Summariser"
summarise = "summarise"
summarised = "summarised"
summarisation = "summarisation"
behaviour = "behaviour"
Behaviour = "Behaviour"
initialise = "initialise"
recognise = "recognise"

[files]
extend-exclude = [
"node_modules",
Expand Down
128 changes: 128 additions & 0 deletions docs/adr/0046-package-ooda-loop-plugin-as-standalone-plugin-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
id: ADR-0046
title: Package the OODA Loop plugin as a standalone plugin group under plugins/ooda/
status: accepted
date: 2026-05-13
deciders:
- architect
consulted:
- pm
- ux-designer
informed:
- repo maintainers
supersedes: []
superseded-by: []
tags: [plugins, ooda, architecture, packaging]
---

# ADR-0046 — Package the OODA Loop plugin as a standalone plugin group under plugins/ooda/

## Status

Accepted

## Context

The OODA Loop plugin (PRD-OODA-001) introduces a continuous situation-awareness capability — a manually invoked daily brief powered by Observe, Orient, Decide, and Act (Tier 1) phases — for use between Specorator feature cycles. The plugin needs a packaging home within the repository that satisfies four constraints simultaneously:

1. **ADR-0026 constraint:** the v1.0 workflow track taxonomy is frozen. Adding a new first-party lifecycle track (Stages 1–11 equivalent) before v1.0 supersedes ADR-0026, which requires a separate ADR and human approval. The OODA loop is explicitly not a lifecycle track — it operates between feature cycles.

2. **ADR-0036 contract:** the plugin manifest standard establishes `plugins/<group>/` as the canonical location for capability groups. Each group ships `manifest.md` and `schema.json`. The OODA plugin must conform to this standard.

3. **Distinct capability surface:** the OODA loop has its own agent files (four dedicated agents), its own tool requirements (Haiku for Observe/Act, Sonnet for Orient/Decide), its own permissions model (`settings.json` with Tier 1 allow rules and Tier 3 deny rules), and its own release cadence. It does not map cleanly to any of the 12 existing plugin groups.

4. **No `.claude/` modification:** ADR-0036 requires that new plugin groups do not move, rename, or alter files under `.claude/agents/`, `.claude/skills/`, or `.claude/commands/`. The OODA plugin files live inside `plugins/ooda/` and are referenced by relative path from the manifest — they are not added to `.claude/`.

Three placement options were evaluated:

- **A** — Place OODA agent files inside `.claude/agents/` (same pattern as lifecycle agents). This would implicitly make OODA a lifecycle participant, blurring the distinction between the continuous loop and the feature lifecycle. It also violates ADR-0036's additive-only rule for the plugin surface.
- **B** — Merge OODA into an existing plugin group (e.g., `developer-tools`). The `developer-tools` group covers operational bots and utilities; the OODA loop is a distinct user-invoked workflow with its own memory, agents, and permission model. Merging would make the group's scope incoherent.
- **C** — Create a new `plugins/ooda/` group as a standalone addition to the `plugins/` surface. This is additive under ADR-0036, does not require superseding ADR-0026, and gives the plugin its own versioning, manifest, and schema.

## Decision

We package the OODA Loop plugin as a new standalone group at `plugins/ooda/`, conforming to the ADR-0036 manifest standard.

The directory layout is:

```
plugins/ooda/
├── .claude-plugin/
│ └── plugin.json ← plugin manifest (id, version, entry_skill)
├── manifest.md ← ADR-0036 required: human-readable capability declaration
├── schema.json ← ADR-0036 required: machine-readable MCP tool registry input
├── skills/
│ └── ooda/
│ └── SKILL.md ← entry point: /ooda:brief
├── agents/
│ ├── observe.md ← Haiku; tools: Read, Bash, MCP
│ ├── orient.md ← Sonnet; tools: Read only
│ ├── decide.md ← Sonnet; tools: Read only
│ └── act.md ← Haiku; tools: Read, Edit, Bash, MCP
├── monitors/
│ └── monitors.json ← v2+: background signal watchers (stub in v1)
├── hooks/
│ └── hooks.json ← PreToolUse hook for Act gate (v2+; stub in v1)
└── settings.json ← Tier 1 allow rules + Tier 3 deny rules
```

The OODA plugin is explicitly classified as a **companion plugin** — not a lifecycle track — in both `manifest.md` and in the `docs/adr/README.md` index. The plugin is invoked between feature cycles via `/ooda:brief`; it never replaces or wraps a lifecycle stage (Stages 1–11).

Workspace-level artifacts written by the plugin (`ooda-sources.yaml`, `memory/`, `briefs/`, `ooda-runs/`) live at the workspace root, outside `plugins/ooda/`, to preserve separation between plugin distribution files and per-workspace runtime state.

## Considered options

### Option A — Place OODA agents inside `.claude/agents/`

- Pros: consistent with existing lifecycle agent placement; no new top-level directory inside `plugins/`.
- Cons: violates ADR-0036 additive-only rule; conflates the OODA companion loop with the lifecycle; makes it impossible to version OODA independently; `.claude/agents/` is Claude Code–specific, limiting discoverability by external MCP clients.

### Option B — Merge into existing `plugins/developer-tools` group

- Pros: no new plugin group; reuses existing manifest/schema.
- Cons: `developer-tools` covers operational bots and scheduled utilities, not user-invoked interactive loops; mixing the OODA memory model, agent files, and settings fragment into `developer-tools` makes the group's contract incoherent; independent versioning of OODA is impossible.

### Option C — New `plugins/ooda/` standalone group (chosen)

- Pros: clean ADR-0036 compliance; independent versioning; clear capability boundary; does not require superseding ADR-0026; manifest surfaces the OODA tool set to external MCP clients without ambiguity.
- Cons: adds one more group to `plugins/` (13 groups total vs. 12). The ADR-0036 group list is declared extensible by third-party authors; a first-party addition does not contradict that.

## Consequences

### Positive

- OODA ships as a versioned, standalone, manifest-compliant plugin group, fully discoverable by the Specorator MCP Server (issue #316) via `plugins/ooda/schema.json`.
- ADR-0026 track taxonomy freeze is respected: no new lifecycle track is created.
- The four agent files, `settings.json`, and skill entry point are independently versioned and updatable without modifying any lifecycle artifact.
- Clear boundary: `plugins/ooda/` = distribution files; workspace root = runtime state (`memory/`, `briefs/`, `ooda-runs/`).

### Negative

- `plugins/` now has 13 groups, one more than the 12 established by ADR-0036. The `plugins/README.md` and `docs/sink.md` must be updated to reflect the addition.
- `manifest.md` and `schema.json` are manually kept in sync with agent file changes, per the ADR-0036 Phase 1–2 policy (no build-step validation yet).

### Neutral

- `monitors/monitors.json` and `hooks/hooks.json` ship as stubs in v1. Their presence signals the v2+ extension points without activating them.
- Plugin `settings.json` composes with the project-level `.claude/settings.json` via the Claude Code permission model. OODA deny rules are evaluated before project-level rules, ensuring Tier 3 operations remain blocked regardless of project permission mode.

## Compliance

- `plugins/ooda/manifest.md` frontmatter must include required keys: `name`, `version`, `description`, `capabilities`, `mcp_tools`.
- `plugins/ooda/schema.json` must be valid JSON (checked by `npm run verify:json`).
- `docs/sink.md` must list `plugins/ooda/` as a layout entry owned by the template maintainer.
- `docs/adr/README.md` index row added for this ADR.
- Design document `specs/ooda-loop-plugin/design.md` references this ADR in the C6 Key Decisions table and in frontmatter `adrs:`.

## References

- PRD-OODA-001 — OODA Loop Plugin requirements
- DESIGN-OODA-001 — OODA Loop Plugin design (Part C, C6 Key Decisions)
- RESEARCH-OODA-001 Q7 — Plugin packaging research question
- ADR-0026 — Freeze the v1.0 workflow track taxonomy (constraint)
- ADR-0036 — Adopt plugin manifests as the Specorator capability contract (standard to follow)
- `plugins/README.md` — plugin surface entry point

---

> **ADR bodies are immutable.** To change a decision, supersede it with a new ADR; only the predecessor's `status` and `superseded-by` pointer fields may be updated.
128 changes: 128 additions & 0 deletions docs/adr/0047-adopt-two-file-hybrid-orient-memory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
id: ADR-0047
title: Adopt two-file hybrid orient memory for the OODA Loop plugin
status: accepted
date: 2026-05-13
deciders:
- architect
consulted:
- pm
- analyst
informed:
- repo maintainers
supersedes: []
superseded-by: []
tags: [ooda, memory, orient, architecture, persistence]
---

# ADR-0047 — Adopt two-file hybrid orient memory for the OODA Loop plugin

## Status

Accepted

## Context

The Orient phase of the OODA Loop plugin must accumulate project-state context across daily runs to detect "new since last check-in" deltas, flag belief decay, and identify anomalies between observed state and recorded beliefs. Without persistent memory, each brief is synthesised from scratch — fully correct for day one, but incapable of answering "what changed since yesterday?" after that.

Four strategies were evaluated for storing Orient memory in a repo-native, file-based environment:

1. **Stateless per-run** — no persistence; Orient reads only the current Observe output.
2. **Two-file hybrid** — a capped working-state file (`memory/state.md`, ≤3,000 tokens) plus an append-only run log (`memory/events.jsonl`). A summariser agent re-derives `state.md` from the last 14 JSONL entries when the token limit is exceeded.
3. **Single growing file** — Orient appends to a single `memory/state.md` with no compression.
4. **Temporal knowledge graph** — facts stored as graph nodes with dual timestamps (e.g., Zep / Graphiti); retrieval via BM25 + cosine + graph traversal.

The repo-native constraint eliminates option 4 (requires an external graph database). Option 3 is eliminated by NFR-OODA-002 (state.md must stay ≤3,000 tokens after any summarisation pass). Option 1 is eliminated by the feature's core value proposition: "new since last check-in" detection requires at minimum a prior state snapshot to diff against.

The MemMachine architecture (arXiv:2604.04853) validates the two-file hybrid pattern: raw episodic log as immutable ground truth + LLM-derived working summary as the loaded context. The paper reports 93% accuracy at 80% token cost reduction versus naive full-history loading. This project's existing `.claude/memory/MEMORY.md` pattern implements the same principle (index file + per-topic detail files), confirming operational feasibility in this codebase.

A second critical design constraint is **summarisation drift**: if the summariser reads a prior summary as its input, each compression cycle compresses the previous compression's lossy output — amplifying errors over time. The two-file hybrid prevents this by making JSONL the immutable ground truth that the summariser always reads from scratch, never the prior `state.md`.

The Orient context budget is sized at ≤20,000 tokens for multi-step reasoning tasks. `state.md` at ≤3,000 tokens plus `observe.md` (typically 2,000–5,000 tokens per run) keeps Orient well within this budget across any project age.

## Decision

We adopt the two-file hybrid as the canonical Orient memory model for the OODA Loop plugin.

**File 1 — `memory/state.md`** (working state; loaded every run):
- YAML frontmatter: `version` (integer, incremented by Orient on each write), `last_summarised` (ISO date, updated when summariser runs), `token_estimate` (integer, computed before each run).
- Sections: `## Orientation summary` (free prose, ≤2,000 tokens), `## Open blockers` (entries with `id`, `description`, `last_seen`, `confidence`), `## Recent decisions` (entries with `id`, `description`, `date`), `## focus_signals` (list of high-priority source names), `## Pinned Constraints` (human-maintained; never touched by agents).
- Token budget: ≤3,000 tokens. Summariser is triggered when `token_estimate` exceeds this threshold at the start of a run.

**File 2 — `memory/events.jsonl`** (append-only run log; never loaded directly by agents):
- One JSON line per completed run: `run_ts`, `sources`, `orient_summary`, `decisions`, `user_feedback`.
- Read exclusively by the summariser agent. Agents in the Observe, Orient, Decide, and Act phases do not read this file.
- Serves as immutable ground truth for reconstruction, audit, and the v3+ upgrade path to SQLite+BM25.

**Summariser trigger:** when `state.md` `token_estimate` exceeds 3,000 at the start of a run, the orchestrator invokes the summariser before dispatching Observe. The summariser reads the last 14 entries from `events.jsonl` only, re-derives `state.md` from scratch, and preserves the `Pinned Constraints` section verbatim.

**Summariser anti-drift rule:** the summariser must not load the existing `state.md` as an input. The only inputs to summarisation are `events.jsonl` entries and the preserved `Pinned Constraints` text extracted separately.

**Belief decay:** entries in `## Open blockers` and `## Recent decisions` carry a `last_seen` field. Orient is instructed to lower the confidence score of any entry whose `last_seen` date is 7 or more days before the current run date and annotate it as requiring human review.

**Upgrade path:** the JSONL structure is designed for a non-breaking migration to SQLite+BM25 (via the memweave pattern). The field names and types in `events.jsonl` entries are stable across v1–v3. No format change is required to adopt a query layer over the log.

## Considered options

### Option A — Stateless per-run Orient

- Pros: zero state management complexity; no summarisation drift or context poisoning risk; simplest implementation.
- Cons: no "new since last check-in" detection (cannot diff against prior state); Orient never improves over time; each brief looks identical to the previous one; fundamentally violates Boyd's Orient model, which depends on accumulated experience.
- **Rejected:** eliminates the feature's core value proposition.

### Option B — Two-file hybrid with cadenced summarisation (chosen)

- Pros: ground truth never overwritten; context budget stays bounded at any project age; fully repo-native; human-readable; git-diffable; rollback via `git revert`; upgrade path to SQLite+BM25 is non-breaking.
- Cons: requires disciplined JSONL structure; noisy feedback entries degrade the summariser; summariser adds a periodic LLM call (~$0.01–$0.05); compression loss risk if summariser prompt is poorly designed (mitigated by retaining JSONL as ground truth).

### Option C — Single growing `state.md`

- Pros: simplest possible persistent model; no JSONL management.
- Cons: violates NFR-OODA-002 (≤3,000 tokens) after ~14 days of daily runs; Orient context degrades as file grows; no clean boundary between current working state and historical entries; no immutable ground truth for reconstruction.
- **Rejected:** fails NFR-OODA-002 by construction.

### Option D — Temporal knowledge graph (Zep / Graphiti)

- Pros: best handling of evolving facts; best retrieval accuracy (94.8% on DMR benchmark); purpose-built for "things change over time" data.
- Cons: requires graph database (Neo4j or Graphiti embedded) — not repo-native; breaks the "no external services" constraint from idea.md; overkill for a single-project daily brief with <100 distinct facts; significantly higher operational complexity.
- **Rejected:** violates the repo-native, no-external-services constraint; appropriate for v3+ multi-repo scope if needed.

## Consequences

### Positive

- Orient memory is bounded, git-native, and human-inspectable at all times.
- The `Pinned Constraints` section gives the human maintainer a durable override surface that survives all compression cycles.
- `events.jsonl` as immutable ground truth means any summarisation error is recoverable by re-running the summariser.
- The user feedback field in JSONL feeds the summariser's `orient_priority` update logic, closing the OODA feedback loop without additional infrastructure.

### Negative

- Summariser prompt quality matters: a poorly designed prompt produces lossy `state.md` that silently drops low-salience but relevant facts. Mitigated by retaining JSONL.
- Two files to manage vs. one. Users unfamiliar with the model may be tempted to edit `events.jsonl` by hand (documented as forbidden in IA section of design.md).
- The 14-entry window in the summariser means context older than ~14 days can be lost if the summariser is triggered frequently. The archive pass to `memory/archive/YYYY-MM.md` (v2+) addresses long-term retention.

### Neutral

- `memory/` is a new workspace-level directory introduced by this plugin. If other plugins need per-workspace persistent memory, `memory/` under the workspace root is the precedent to follow.
- The summariser LLM call is bounded: one Sonnet call per trigger event, typically once per 1–2 weeks of daily use. Cost is within the NFR-OODA-005 per-run budget envelope on trigger days (adds ~$0.01–$0.05 to that run).

## Compliance

- Orient agent system prompt must include an explicit rule: "Do not read `memory/events.jsonl`. Your memory inputs are `memory/state.md` and the current run's `ooda-runs/<timestamp>/observe.md` only."
- Summariser agent system prompt must include an explicit rule: "Do not read the existing `memory/state.md` as a summarisation input. Read only the last 14 entries from `memory/events.jsonl`. Extract and preserve the `## Pinned Constraints` section from the prior `state.md` as a literal copy."
- `spec.md` for the OODA plugin must specify the `state.md` frontmatter schema, the `events.jsonl` entry schema, and the summariser trigger condition with the ≤3,000 token threshold.
- NFR-OODA-002 test: after 7 simulated daily runs on a reference workspace, `state.md` token count must be ≤3,000. This is a release criterion in PRD-OODA-001.

## References

- PRD-OODA-001 — OODA Loop Plugin requirements (REQ-OODA-008, REQ-OODA-009, REQ-OODA-011, REQ-OODA-012, REQ-OODA-019, REQ-OODA-021; NFR-OODA-002)
- RESEARCH-OODA-001 Q3 — Orient memory research question and Alternative B recommendation
- DESIGN-OODA-001 — OODA Loop Plugin design (Part C, C3 Data model, C6 Key Decisions)
- arXiv:2604.04853 — MemMachine: A Ground-Truth-Preserving Memory System (validates the two-file hybrid pattern)
- arXiv:2310.08560 — MemGPT: Towards LLMs as Operating Systems (paging / working memory analogy)
- `.claude/memory/MEMORY.md` — existing in-repo memory pattern this design extends

---

> **ADR bodies are immutable.** To change a decision, supersede it with a new ADR; only the predecessor's `status` and `superseded-by` pointer fields may be updated.
Loading
Loading