Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 08df3a4697 | |||
| 90761afe0c | |||
| 32723c6022 | |||
| 9d05ece3f0 | |||
| 5cd5d99198 | |||
| 0c2df43a2f | |||
| c04f4eedc9 | |||
| e2aa62e597 | |||
| 50aa553cea | |||
| 5742e2c334 | |||
| 0362c192d4 | |||
| 57542dcbc1 | |||
| dda49cd821 | |||
| 4a8f842a37 | |||
| c1e2680bc6 | |||
| 93bf49479b | |||
| 1641a3165d | |||
| c5798f58e4 | |||
| 7424893758 | |||
| d220bae007 | |||
| 843cf4eee0 | |||
| 7058859e5c | |||
| eeb4eee5ef | |||
| 3b2edd4a9e | |||
| 2400ff4890 | |||
| df899abd98 | |||
| b1fceeebc8 | |||
| fe795ca750 | |||
| 1d0999eabb |
20
.claude/rules/agents.md
Normal file
20
.claude/rules/agents.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
## Kaizen Agents
|
||||||
|
|
||||||
|
Specialized agent personas available on demand via the state-hub MCP.
|
||||||
|
|
||||||
|
**Discover:** `list_kaizen_agents()` — returns all agents with name, description, category
|
||||||
|
**Load:** `get_kaizen_agent("tdd-workflow")` — returns full instructions; read and follow them
|
||||||
|
|
||||||
|
Common agents:
|
||||||
|
|
||||||
|
| Agent | Category | When to use |
|
||||||
|
|-------|----------|-------------|
|
||||||
|
| `tdd-workflow` | testing | Step-by-step TDD8 workflow for any feature |
|
||||||
|
| `code-refactoring` | quality | Code quality analysis and safe refactoring |
|
||||||
|
| `test-maintenance` | testing | Diagnose and fix failing tests |
|
||||||
|
| `requirements-engineering` | process | Prevent interface/mock mismatches upfront |
|
||||||
|
| `keepaTodofile` | process | Maintain TODO.md during work |
|
||||||
|
| `project-management` | process | Track status, determine next steps |
|
||||||
|
| `datamodel-optimization` | quality | Optimize dataclasses and data structures |
|
||||||
|
|
||||||
|
All 17 agents: call `list_kaizen_agents()` for the full list.
|
||||||
@@ -1,29 +1,8 @@
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
kaizen-agentic has two distinct layers:
|
<!-- TODO: Describe the key design decisions and component structure.
|
||||||
|
Key modules, data flows, external integrations, state machines, etc. -->
|
||||||
|
|
||||||
### 1. Python framework (`src/kaizen_agentic/`)
|
## Quick Reference
|
||||||
|
|
||||||
- **`core.py`** — `Agent` (abstract base) + `AgentConfig` (dataclass). Tracks performance, supports config updates, implements kaizen interface.
|
`~/state-hub/mcp_server/TOOLS.md` — MCP tool reference
|
||||||
- **`optimization.py`** — `OptimizationLoop` (runs improvement cycles, detects trends, generates recommendations) + `PerformanceMetrics` (execution time, success rate, quality scores).
|
|
||||||
- **`metrics.py`** — `MetricsStore` + `OptimizerStore` (project-scoped `.kaizen/metrics/` per ADR-004).
|
|
||||||
|
|
||||||
### 2. Agent definitions (`agents/` — 20 files)
|
|
||||||
|
|
||||||
Markdown instruction sets read and followed by Claude. Not executables. Naming convention: `agent-{name}.md`.
|
|
||||||
Packaged copies live in `src/kaizen_agentic/data/agents/` for `pip install` distribution.
|
|
||||||
|
|
||||||
| Category | Agents |
|
|
||||||
|----------|--------|
|
|
||||||
| Testing | `tdd-workflow`, `test-maintenance`, `testing-efficiency` |
|
|
||||||
| Quality | `code-refactoring`, `datamodel-optimization` |
|
|
||||||
| Process | `requirements-engineering`, `keepaTodofile`, `keepaChangelog`, `keepaContributingfile`, `project-management`, `priority-evaluation`, `scope-analyst` |
|
|
||||||
| Infrastructure | `setupRepository`, `tooling-optimization`, `sys-medic` |
|
|
||||||
| Release | `releaseManager` |
|
|
||||||
| Docs | `claude-documentation` |
|
|
||||||
| Support | `wisdom-encouragement` |
|
|
||||||
| Meta | `coach`, `optimization` |
|
|
||||||
|
|
||||||
### Custodian integration
|
|
||||||
|
|
||||||
The state-hub MCP resolves the agents directory via `host_paths[hostname]` → `local_path`. Tools: `list_kaizen_agents(category?)`, `get_kaizen_agent(name)`.
|
|
||||||
|
|||||||
50
.claude/rules/credential-routing.md
Normal file
50
.claude/rules/credential-routing.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Credential and access routing
|
||||||
|
|
||||||
|
**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect**
|
||||||
|
for inference. Run this check **before** requesting secrets, API keys, SSH access,
|
||||||
|
login tokens, or database passwords — in any repo, not only `ops-warden`.
|
||||||
|
|
||||||
|
ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every
|
||||||
|
other credential need belongs to another subsystem. **Do not** message
|
||||||
|
`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key.
|
||||||
|
|
||||||
|
### Lookup (do this first)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
warden route find "<describe your need>" --json
|
||||||
|
warden route show <catalog-id> --json
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`).
|
||||||
|
|
||||||
|
| Agent runtime | How to orient |
|
||||||
|
| --- | --- |
|
||||||
|
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=kaizen-agentic` is for coordination, not secret vending |
|
||||||
|
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workstreams; **still** use `warden route` for credential ownership |
|
||||||
|
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
|
||||||
|
|
||||||
|
### Quick routing table
|
||||||
|
|
||||||
|
| I need… | Owner | ops-warden executes? |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` |
|
||||||
|
| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only |
|
||||||
|
| Login / OIDC / MFA | key-cape / Keycloak | No — route only |
|
||||||
|
| Authorization decision | flex-auth | No — route only |
|
||||||
|
| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` |
|
||||||
|
| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only |
|
||||||
|
|
||||||
|
### Anti-patterns (do not do these)
|
||||||
|
|
||||||
|
- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc.
|
||||||
|
- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist
|
||||||
|
- Pasting secrets into Git, State Hub, workplans, logs, or chat
|
||||||
|
|
||||||
|
### Other capabilities (reuse-surface)
|
||||||
|
|
||||||
|
Non-credential capabilities are usually discovered through **reuse-surface** federation
|
||||||
|
(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in
|
||||||
|
every repo's agent instructions because it is high-frequency, high-risk, and easy to
|
||||||
|
get wrong.
|
||||||
|
|
||||||
|
**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml`
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
## First Session Protocol
|
## First Session Protocol
|
||||||
|
|
||||||
Triggered when `get_domain_summary("custodian")` shows **no workstreams**.
|
Triggered when `get_domain_summary("agents")` shows **no workstreams**.
|
||||||
The project is registered but work has not yet been structured.
|
The project is registered but work has not yet been structured.
|
||||||
|
|
||||||
**Step 1 — Read, don't write**
|
**Step 1 — Read, don't write**
|
||||||
- `~/the-custodian/canon/projects/custodian/project_charter_v0.1.md` — purpose, scope
|
- `~/the-custodian/canon/projects/agents/project_charter_v0.1.md` — purpose, scope
|
||||||
- `~/the-custodian/canon/projects/custodian/roadmap_v0.1.md` — planned phases
|
- `~/the-custodian/canon/projects/agents/roadmap_v0.1.md` — planned phases
|
||||||
- Scan repo root: README, directory structure, existing code or docs
|
- Scan repo root: README, directory structure, existing code or docs
|
||||||
|
|
||||||
**Step 2 — Survey in-progress work**
|
**Step 2 — Survey in-progress work**
|
||||||
@@ -17,20 +17,20 @@ roadmap phase. **Wait for approval before creating.**
|
|||||||
|
|
||||||
**Step 4 — Create workplan file first, then DB record (ADR-001)**
|
**Step 4 — Create workplan file first, then DB record (ADR-001)**
|
||||||
```
|
```
|
||||||
workplans/kaizen-agentic-WP-NNNN-<slug>.md ← write this first
|
workplans/KAIZEN-WP-NNNN-<slug>.md ← write this first
|
||||||
```
|
```
|
||||||
Then register in the hub:
|
Then register in the hub:
|
||||||
```
|
```
|
||||||
create_workstream(topic_id="cee7bedf-2b48-46ef-8601-006474f2ad7a", title="...", owner="...", description="...")
|
create_workstream(topic_id="64418556-3206-457a-ba29-6884b5b12cf3", title="...", owner="...", description="...")
|
||||||
create_task(workstream_id="<id>", title="...", priority="high|medium|low")
|
create_task(workstream_id="<id>", title="...", priority="high|medium|low")
|
||||||
```
|
```
|
||||||
|
|
||||||
**Step 5 — Record the setup**
|
**Step 5 — Record the setup**
|
||||||
```
|
```
|
||||||
add_progress_event(
|
add_progress_event(
|
||||||
summary="First session: structured custodian into N workstreams, M tasks",
|
summary="First session: structured agents into N workstreams, M tasks",
|
||||||
event_type="milestone",
|
event_type="milestone",
|
||||||
topic_id="cee7bedf-2b48-46ef-8601-006474f2ad7a",
|
topic_id="64418556-3206-457a-ba29-6884b5b12cf3",
|
||||||
detail={"workstreams": [...], "tasks_created": M}
|
detail={"workstreams": [...], "tasks_created": M}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
This repo owns **kaizen-agentic** only. It does not own:
|
This repo owns **kaizen-agentic** only. It does not own:
|
||||||
|
|
||||||
- State-hub MCP integration code → `the-custodian/state-hub/mcp_server/server.py`
|
<!-- TODO: List what belongs in adjacent repos, e.g.:
|
||||||
- Agent discovery tools (`list_kaizen_agents`, `get_kaizen_agent`) → `the-custodian`
|
- SSH key management → railiance-infra/
|
||||||
- Custodian coordination and workplan tracking → `the-custodian`
|
- State hub code → state-hub/
|
||||||
- Deployment to custodiancore → `ops-bridge`
|
-->
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
## Repo Identity
|
**Purpose:** AI-assisted development quality toolchain. Provides pre-commit hooks, CI/CD pipeline automation, usage telemetry, and CLI improvement scaffolding for the custodian domain.
|
||||||
|
|
||||||
**Purpose:** kaizen-agentic — AI agent development framework embracing kaizen (continuous improvement). Provides 17 specialized Claude Code companion agents plus an OptimizationLoop framework for continuous performance measurement and refinement.
|
**Domain:** agents
|
||||||
|
|
||||||
**Domain:** custodian
|
|
||||||
**Repo slug:** kaizen-agentic
|
**Repo slug:** kaizen-agentic
|
||||||
**Topic ID:** cee7bedf-2b48-46ef-8601-006474f2ad7a
|
**Topic ID:** 64418556-3206-457a-ba29-6884b5b12cf3
|
||||||
|
|
||||||
**Custodian integration:** This repo is the single source of truth for all kaizen agents. The state-hub MCP exposes `list_kaizen_agents()` and `get_kaizen_agent(name)` tools so any connected session can discover and load agents without a local copy.
|
|
||||||
|
|||||||
@@ -1,29 +1,50 @@
|
|||||||
## Session Protocol
|
## Session Protocol
|
||||||
|
|
||||||
State Hub: http://127.0.0.1:8000
|
Dev Hub (State Hub API): http://127.0.0.1:8000
|
||||||
|
MCP server name in `~/.claude.json`: `dev-hub`
|
||||||
|
|
||||||
**Step 1 — Orient**
|
**Step 1 — Orient**
|
||||||
|
|
||||||
|
Read the offline-safe brief first — it works without a live hub connection:
|
||||||
|
```bash
|
||||||
|
cat .custodian-brief.md
|
||||||
```
|
```
|
||||||
get_domain_summary("custodian")
|
Then call the MCP tool for richer cross-domain context when MCP tools are exposed:
|
||||||
```
|
```
|
||||||
If offline: `cd ~/the-custodian/state-hub && make api`
|
get_domain_summary("agents")
|
||||||
|
```
|
||||||
|
If MCP tools are unavailable in the current agent session, use the REST API:
|
||||||
|
```bash
|
||||||
|
curl -s "http://127.0.0.1:8000/state/summary" | python3 -m json.tool
|
||||||
|
```
|
||||||
|
If the hub is offline: `cd ~/state-hub && make api`
|
||||||
|
|
||||||
**Step 2 — Check inbox**
|
**Step 2 — Check inbox**
|
||||||
|
With MCP tools:
|
||||||
```
|
```
|
||||||
get_messages(to_agent="kaizen-agentic", unread_only=True)
|
get_messages(to_agent="kaizen-agentic", unread_only=True)
|
||||||
```
|
```
|
||||||
Mark read with `mark_message_read(message_id)`. Reply or act on coordination
|
Mark read with `mark_message_read(message_id)`. Reply or act on coordination
|
||||||
requests before proceeding.
|
requests before proceeding.
|
||||||
|
|
||||||
|
Without MCP tools:
|
||||||
|
```bash
|
||||||
|
curl -s "http://127.0.0.1:8000/messages/?to_agent=kaizen-agentic&unread_only=true" \
|
||||||
|
| python3 -m json.tool
|
||||||
|
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
|
||||||
|
-H "Content-Type: application/json" -d '{}'
|
||||||
|
```
|
||||||
|
|
||||||
**Step 3 — Scan workplans**
|
**Step 3 — Scan workplans**
|
||||||
```bash
|
```bash
|
||||||
ls workplans/
|
ls workplans/
|
||||||
```
|
```
|
||||||
For each file with `status: active`, note pending `todo`/`in_progress` tasks.
|
For each file with `status: ready`, `active`, or `blocked`, note pending
|
||||||
|
`wait`/`todo`/`progress` tasks.
|
||||||
|
|
||||||
**Step 4 — Present brief**
|
**Step 4 — Present brief**
|
||||||
|
|
||||||
1. **Active workstreams** for `custodian` — title, task counts, blocking decisions
|
1. **Active workstreams** for `agents` — title, task counts, blocking decisions
|
||||||
2. **Pending tasks** from `workplans/` + any `[repo:kaizen-agentic]` hub tasks
|
2. **Pending tasks** from `workplans/` + any `[repo:kaizen-agentic]` hub tasks
|
||||||
3. **Goal guidance** — if `goal_guidance` in summary:
|
3. **Goal guidance** — if `goal_guidance` in summary:
|
||||||
- `needs_workplan`: surface as top action — *"Repo goal '{title}' has no workplan yet"*
|
- `needs_workplan`: surface as top action — *"Repo goal '{title}' has no workplan yet"*
|
||||||
@@ -39,10 +60,26 @@ If no workstreams: follow First Session Protocol (`first-session.md`).
|
|||||||
> are First Session Protocol only. Work structure belongs in repo files (ADR-001).
|
> are First Session Protocol only. Work structure belongs in repo files (ADR-001).
|
||||||
|
|
||||||
**Session close:**
|
**Session close:**
|
||||||
|
With MCP tools:
|
||||||
```
|
```
|
||||||
add_progress_event(summary="...", topic_id="cee7bedf-2b48-46ef-8601-006474f2ad7a", workstream_id="<uuid>")
|
add_progress_event(summary="...", topic_id="64418556-3206-457a-ba29-6884b5b12cf3", workstream_id="<uuid>")
|
||||||
```
|
```
|
||||||
If workplan files were modified:
|
Without MCP tools:
|
||||||
```bash
|
```bash
|
||||||
cd ~/the-custodian/state-hub && make fix-consistency REPO=kaizen-agentic
|
curl -s -X POST http://127.0.0.1:8000/progress/ \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"topic_id":"64418556-3206-457a-ba29-6884b5b12cf3","workstream_id":"<uuid>","event_type":"note","summary":"what changed","author":"codex"}'
|
||||||
```
|
```
|
||||||
|
If workplan files were modified, ensure the local copy is up to date first:
|
||||||
|
```bash
|
||||||
|
git -C <repo_path> pull --ff-only
|
||||||
|
cd ~/state-hub && make fix-consistency REPO=kaizen-agentic
|
||||||
|
```
|
||||||
|
For repos where implementation runs on a remote machine (e.g. CoulombCore),
|
||||||
|
use the combined target which pulls before fixing:
|
||||||
|
```bash
|
||||||
|
cd ~/state-hub && make fix-consistency-remote REPO=kaizen-agentic
|
||||||
|
```
|
||||||
|
**C-15** (DB task ahead of file) is normal in multi-machine workflows — writeback
|
||||||
|
will sync the file to match DB. **C-16** (repo behind remote) blocks all writes
|
||||||
|
until you pull — intentional to prevent clobbering remote progress.
|
||||||
|
|||||||
@@ -1,43 +1,19 @@
|
|||||||
## Stack and Commands
|
## Stack
|
||||||
|
|
||||||
**Language:** Python 3.8+
|
<!-- TODO: Fill in language, frameworks, and key dependencies -->
|
||||||
**Package manager:** uv / pip (`.venv/`)
|
- **Language:**
|
||||||
**Test runner:** pytest
|
- **Key deps:**
|
||||||
**Linter/formatter:** flake8 (100-char), black (88-char), mypy (strict)
|
|
||||||
|
|
||||||
### Essential commands
|
## Dev Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make setup-complete # First-time setup: venv + package + dev deps
|
# TODO: Fill in the standard commands for this repo
|
||||||
source .venv/bin/activate
|
|
||||||
make test # Run full test suite
|
# Install dependencies
|
||||||
make lint # flake8 linting
|
|
||||||
make format # black formatting
|
# Run tests
|
||||||
make clean # Remove build artifacts
|
|
||||||
|
# Lint / type check
|
||||||
|
|
||||||
|
# Build / package (if applicable)
|
||||||
```
|
```
|
||||||
|
|
||||||
### TDD workflow
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make tdd-start ISSUE=X # Start issue with requirements validation
|
|
||||||
make tdd-add-test # Add test to current workspace
|
|
||||||
make tdd-status # Show workspace state
|
|
||||||
make tdd-finish # Move tests to main suite
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue management
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make issue-list # All issues (Gitea)
|
|
||||||
make issue-list-open # Open backlog
|
|
||||||
make issue-show ISSUE=X # Issue detail
|
|
||||||
make issue-create TITLE='...' BODY='...'
|
|
||||||
```
|
|
||||||
|
|
||||||
Run `make help` to see all available targets.
|
|
||||||
|
|
||||||
### Core dependencies (pyproject.toml)
|
|
||||||
|
|
||||||
- `pyyaml>=6.0` — YAML config
|
|
||||||
- `click>=8.0.0` — CLI framework
|
|
||||||
- `pydantic>=2.0.0` — Data validation
|
|
||||||
|
|||||||
@@ -1,12 +1,40 @@
|
|||||||
## Workplan Convention (ADR-001)
|
## Workplan Convention (ADR-001)
|
||||||
|
|
||||||
File location: `workplans/kaizen-agentic-WP-NNNN-<slug>.md`
|
File location: `workplans/KAIZEN-WP-NNNN-<slug>.md`
|
||||||
ID prefix: `KAIZEN-WP`
|
ID prefix: `KAIZEN-WP-`
|
||||||
|
|
||||||
Work items originate as files in this repo **before** being registered in the hub.
|
Work items originate as files in this repo **before** being registered in the hub.
|
||||||
|
|
||||||
|
Canonical workplan/workstream frontmatter statuses are:
|
||||||
|
`proposed`, `ready`, `active`, `blocked`, `backlog`, `finished`, `archived`.
|
||||||
|
Use `proposed` for a newly drafted plan, `ready` after review against current
|
||||||
|
repo state, and `finished` when implementation is complete. `stalled` and
|
||||||
|
`needs_review` are derived health labels, not stored statuses.
|
||||||
|
|
||||||
|
Closed workplans may be moved to `workplans/archived/` with a completion-date
|
||||||
|
prefix: `YYMMDD-KAIZEN-WP-NNNN-<slug>.md`. The frontmatter id remains
|
||||||
|
unchanged; the prefix is only for quick visual reference.
|
||||||
|
|
||||||
|
Small opportunistic tasks discovered during another session use **Ad Hoc Tasks**:
|
||||||
|
`workplans/ADHOC-YYYY-MM-DD.md`, workstream slug `adhoc-YYYY-MM-DD`, and task ids
|
||||||
|
`ADHOC-YYYY-MM-DD-T01`, `T02`, etc. Use adhocs only for low-risk work completed
|
||||||
|
directly. Promote anything requiring analysis, design, approval, dependencies, or
|
||||||
|
multiple planned phases into a normal workplan.
|
||||||
|
|
||||||
Ecosystem todos from other agents arrive as `[repo:kaizen-agentic]` hub tasks —
|
Ecosystem todos from other agents arrive as `[repo:kaizen-agentic]` hub tasks —
|
||||||
visible at session start. Pick one up by creating the workplan file, then registering
|
visible at session start. Pick one up by creating the workplan file, then registering
|
||||||
the workstream.
|
the workstream.
|
||||||
|
|
||||||
|
Task blocks use this shape:
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-NNNN-T01
|
||||||
|
status: wait | todo | progress | done | cancel
|
||||||
|
priority: high | medium | low
|
||||||
|
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
|
||||||
|
```
|
||||||
|
|
||||||
|
Status progression is `todo` → `progress` → `done`; use `wait` for waiting or
|
||||||
|
blocked work and `cancel` for stopped work.
|
||||||
|
|
||||||
<!-- Ralph Loop rules and HEUREKA sequence: ~/.claude/CLAUDE.md — do not duplicate here -->
|
<!-- Ralph Loop rules and HEUREKA sequence: ~/.claude/CLAUDE.md — do not duplicate here -->
|
||||||
|
|||||||
18
.custodian-brief.md
Normal file
18
.custodian-brief.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<!-- custodian-brief: generated by fix-consistency — do not edit manually -->
|
||||||
|
# Custodian Brief — kaizen-agentic
|
||||||
|
|
||||||
|
**Domain:** custodian
|
||||||
|
**Last synced:** 2026-06-18 13:14 UTC
|
||||||
|
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
|
||||||
|
|
||||||
|
## Active Workstreams
|
||||||
|
|
||||||
|
*(none — repo may need first-session setup)*
|
||||||
|
|
||||||
|
---
|
||||||
|
## MCP Orientation (when available)
|
||||||
|
|
||||||
|
If the state-hub MCP server is reachable, call:
|
||||||
|
`get_domain_summary("custodian")`
|
||||||
|
This provides richer cross-domain context.
|
||||||
|
If the MCP call fails, use this file as your orientation source.
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -45,3 +45,7 @@ htmlcov/
|
|||||||
|
|
||||||
# Backup directories created by optimization scripts
|
# Backup directories created by optimization scripts
|
||||||
agents_backup_*/
|
agents_backup_*/
|
||||||
|
|
||||||
|
# Project-scoped kaizen runtime state (ADR-002, ADR-004)
|
||||||
|
.kaizen/agents/
|
||||||
|
.kaizen/metrics/
|
||||||
|
|||||||
15
.kaizen/schedule.yml
Normal file
15
.kaizen/schedule.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Kaizen scheduled agent execution manifest (ADR-005)
|
||||||
|
# Engagement: coulomb-loop bootstrap — weekly cadence
|
||||||
|
# Regulator promotes cadence per customer engagement policy (ADR-003).
|
||||||
|
# Validate with: kaizen-agentic schedule validate
|
||||||
|
version: '1'
|
||||||
|
timezone: Europe/Berlin
|
||||||
|
agents:
|
||||||
|
coach:
|
||||||
|
cadence: weekly
|
||||||
|
cron: 0 9 * * 1
|
||||||
|
enabled: true
|
||||||
|
optimization:
|
||||||
|
cadence: weekly
|
||||||
|
cron: 0 10 * * 1
|
||||||
|
enabled: true
|
||||||
30
.repo-classification.yaml
Normal file
30
.repo-classification.yaml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Repo classification (Repo Classification Standard v1.0).
|
||||||
|
|
||||||
|
repo_classification:
|
||||||
|
standard: Repo Classification Standard
|
||||||
|
version: '1.0'
|
||||||
|
classified_at: '2026-06-22'
|
||||||
|
classified_by: human
|
||||||
|
category: tooling
|
||||||
|
domain: agents
|
||||||
|
secondary_domains:
|
||||||
|
- infotech
|
||||||
|
capability_tags:
|
||||||
|
- orchestration
|
||||||
|
- automation
|
||||||
|
- coordination
|
||||||
|
- knowledge
|
||||||
|
- documentation
|
||||||
|
business_stake:
|
||||||
|
- technology
|
||||||
|
- product
|
||||||
|
- automation
|
||||||
|
- people
|
||||||
|
- intelligence
|
||||||
|
business_mechanics:
|
||||||
|
- intention
|
||||||
|
- coordination
|
||||||
|
- operation
|
||||||
|
- adaptation
|
||||||
|
notes: 'Digital talent-agency framework: agent personas, project memory, improvement loops,
|
||||||
|
CLI tooling. Primary domain agents (AI-native), infotech secondary.'
|
||||||
219
AGENTS.md
Normal file
219
AGENTS.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# kaizen-agentic — Agent Instructions
|
||||||
|
|
||||||
|
## Repo Identity
|
||||||
|
|
||||||
|
**Purpose:** AI-assisted development quality toolchain. Provides pre-commit hooks, CI/CD pipeline automation, usage telemetry, and CLI improvement scaffolding for the custodian domain.
|
||||||
|
|
||||||
|
**Domain:** agents
|
||||||
|
**Repo slug:** kaizen-agentic
|
||||||
|
**Topic ID:** `64418556-3206-457a-ba29-6884b5b12cf3`
|
||||||
|
**Workplan prefix:** `KAIZEN-WP-`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## State Hub Integration
|
||||||
|
|
||||||
|
The Custodian State Hub tracks work across all domains. Interact via HTTP REST —
|
||||||
|
there is no MCP server for Codex agents.
|
||||||
|
|
||||||
|
| Context | URL |
|
||||||
|
|---------|-----|
|
||||||
|
| Local workstation | `http://127.0.0.1:8000` |
|
||||||
|
| Remote via tunnel | `http://127.0.0.1:18000` |
|
||||||
|
|
||||||
|
### Orient at session start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Offline brief — works without hub connection
|
||||||
|
cat .custodian-brief.md
|
||||||
|
|
||||||
|
# Active workstreams for this domain
|
||||||
|
curl -s "http://127.0.0.1:8000/workstreams/?topic_id=64418556-3206-457a-ba29-6884b5b12cf3&status=active" \
|
||||||
|
| python3 -m json.tool
|
||||||
|
|
||||||
|
# Check inbox
|
||||||
|
curl -s "http://127.0.0.1:8000/messages/?to_agent=kaizen-agentic&unread_only=true" \
|
||||||
|
| python3 -m json.tool
|
||||||
|
```
|
||||||
|
|
||||||
|
Mark a message read:
|
||||||
|
```bash
|
||||||
|
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
|
||||||
|
-H "Content-Type: application/json" -d '{}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log progress (required at session close)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST http://127.0.0.1:8000/progress/ \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"summary": "what was done",
|
||||||
|
"event_type": "note",
|
||||||
|
"author": "codex",
|
||||||
|
"workstream_id": "<uuid>",
|
||||||
|
"task_id": "<uuid>"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Omit `workstream_id` / `task_id` when not applicable.
|
||||||
|
|
||||||
|
### Update task status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"status": "progress"}'
|
||||||
|
# values: wait | todo | progress | done | cancel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flag a task for human review
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"needs_human": true, "intervention_note": "reason"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Protocol
|
||||||
|
|
||||||
|
**Start:**
|
||||||
|
1. `cat .custodian-brief.md` — domain goal and open workstreams (offline-safe)
|
||||||
|
2. Check inbox: `GET /messages/?to_agent=kaizen-agentic&unread_only=true`; mark read
|
||||||
|
3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks
|
||||||
|
4. Check human-needed tasks: `GET /tasks/?needs_human=true`
|
||||||
|
|
||||||
|
**During work:**
|
||||||
|
- Update task statuses in workplan files as tasks progress
|
||||||
|
- Record significant decisions via `POST /decisions/`
|
||||||
|
|
||||||
|
**Close:**
|
||||||
|
1. Update workplan file task statuses to reflect progress
|
||||||
|
2. Log: `POST /progress/` with a summary of what changed
|
||||||
|
3. Note for the custodian operator: after workplan file changes, run from
|
||||||
|
`~/state-hub`:
|
||||||
|
```bash
|
||||||
|
make fix-consistency REPO=kaizen-agentic
|
||||||
|
```
|
||||||
|
This syncs task status from files into the hub DB.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Credential and access routing
|
||||||
|
|
||||||
|
**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect**
|
||||||
|
for inference. Run this check **before** requesting secrets, API keys, SSH access,
|
||||||
|
login tokens, or database passwords — in any repo, not only `ops-warden`.
|
||||||
|
|
||||||
|
ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every
|
||||||
|
other credential need belongs to another subsystem. **Do not** message
|
||||||
|
`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key.
|
||||||
|
|
||||||
|
### Lookup (do this first)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
warden route find "<describe your need>" --json
|
||||||
|
warden route show <catalog-id> --json
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`).
|
||||||
|
|
||||||
|
| Agent runtime | How to orient |
|
||||||
|
| --- | --- |
|
||||||
|
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=kaizen-agentic` is for coordination, not secret vending |
|
||||||
|
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workstreams; **still** use `warden route` for credential ownership |
|
||||||
|
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
|
||||||
|
|
||||||
|
### Quick routing table
|
||||||
|
|
||||||
|
| I need… | Owner | ops-warden executes? |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` |
|
||||||
|
| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only |
|
||||||
|
| Login / OIDC / MFA | key-cape / Keycloak | No — route only |
|
||||||
|
| Authorization decision | flex-auth | No — route only |
|
||||||
|
| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` |
|
||||||
|
| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only |
|
||||||
|
|
||||||
|
### Anti-patterns (do not do these)
|
||||||
|
|
||||||
|
- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc.
|
||||||
|
- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist
|
||||||
|
- Pasting secrets into Git, State Hub, workplans, logs, or chat
|
||||||
|
|
||||||
|
### Other capabilities (reuse-surface)
|
||||||
|
|
||||||
|
Non-credential capabilities are usually discovered through **reuse-surface** federation
|
||||||
|
(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in
|
||||||
|
every repo's agent instructions because it is high-frequency, high-risk, and easy to
|
||||||
|
get wrong.
|
||||||
|
|
||||||
|
**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml`
|
||||||
|
|
||||||
|
<!-- REPO-AGENTS-EXTENSIONS -->
|
||||||
|
<!-- Append repo-specific agent instructions below this marker.
|
||||||
|
The state-hub template sync preserves content after this line. -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workplan Convention (ADR-001)
|
||||||
|
|
||||||
|
Work items originate as files in this repo — not in the hub. The hub is a
|
||||||
|
read/cache/index layer that rebuilds from files.
|
||||||
|
|
||||||
|
**File location:** `workplans/KAIZEN-WP-NNNN-<slug>.md`
|
||||||
|
|
||||||
|
**Archived location:** finished workplans may move to
|
||||||
|
`workplans/archived/YYMMDD-KAIZEN-WP-NNNN-<slug>.md`. The `YYMMDD` prefix is
|
||||||
|
the completion/archive date; the frontmatter `id` does not change.
|
||||||
|
|
||||||
|
**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use
|
||||||
|
`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use
|
||||||
|
this only for low-risk work completed directly; create a normal workplan for
|
||||||
|
anything needing analysis, design, approval, dependencies, or multiple phases.
|
||||||
|
|
||||||
|
**Frontmatter:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
id: KAIZEN-WP-NNNN
|
||||||
|
type: workplan
|
||||||
|
title: "..."
|
||||||
|
domain: agents
|
||||||
|
repo: kaizen-agentic
|
||||||
|
status: proposed | ready | active | blocked | backlog | finished | archived
|
||||||
|
owner: codex
|
||||||
|
topic_slug: ...
|
||||||
|
created: "YYYY-MM-DD"
|
||||||
|
updated: "YYYY-MM-DD"
|
||||||
|
state_hub_workstream_id: "<uuid>" # written by fix-consistency — do not edit
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `proposed` for a new draft, `ready` after review against current repo
|
||||||
|
state, and `finished` after implementation. `stalled` and `needs_review` are
|
||||||
|
derived health labels, not frontmatter statuses.
|
||||||
|
|
||||||
|
**Task block format** (one per `##` section):
|
||||||
|
|
||||||
|
```
|
||||||
|
## Task Title
|
||||||
|
|
||||||
|
` ` `task
|
||||||
|
id: KAIZEN-WP-NNNN-T01
|
||||||
|
status: wait | todo | progress | done | cancel
|
||||||
|
priority: high | medium | low
|
||||||
|
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
|
||||||
|
` ` `
|
||||||
|
|
||||||
|
Task description text.
|
||||||
|
```
|
||||||
|
|
||||||
|
Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work.
|
||||||
|
|
||||||
|
To create a new workplan:
|
||||||
|
1. Write the file following the format above
|
||||||
|
2. Notify the custodian operator to run `make fix-consistency REPO=kaizen-agentic`
|
||||||
|
(or send a message to the hub agent via `POST /messages/`)
|
||||||
56
CHANGELOG.md
56
CHANGELOG.md
@@ -7,6 +7,62 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- **`TODO.md`** — work tracking uses `workplans/` and State Hub (ADR-001); the
|
||||||
|
`keepaTodofile` agent remains available for other projects
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **`metrics record --emit-event`** — publishes `kaizen.metrics.recorded` NATS
|
||||||
|
envelope for activity-core event-driven definitions (optional `nats-py` via
|
||||||
|
`pip install 'kaizen-agentic[events]'`)
|
||||||
|
- **`schedule init --engagement`** — bootstrap presets for customer engagements
|
||||||
|
(`--bootstrap-cadence hourly|daily|weekly`)
|
||||||
|
- **ADR-006** — customer engagement convention (supplier/customer split, playbook)
|
||||||
|
- **Playbook v1** — `docs/integrations/customer-engagement-playbook.md`,
|
||||||
|
`customer-engagement-repo-layout.md`, override manifest design
|
||||||
|
- **Event contract** — `docs/integrations/kaizen-metrics-recorded-event.md`
|
||||||
|
- **Engagement handoff** — `docs/integrations/activity-core-handoff-engagement.md`
|
||||||
|
|
||||||
|
## [1.4.0] - 2026-06-18
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Agent authoring & doc generation (WP-0007)** — `kaizen-agentic create-agent`
|
||||||
|
scaffolds a schema-valid agent; `kaizen-agentic docs generate [--check]`
|
||||||
|
refreshes the CLAUDE.md `## Installed Agents` section idempotently
|
||||||
|
- **Frontmatter schema validation** — `kaizen-agentic validate` now enforces
|
||||||
|
required `name`/`description`/`category`, a known category, and valid
|
||||||
|
`memory`/`model` values with actionable errors
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Idempotent doc regeneration** — `_update_documentation` no longer duplicates
|
||||||
|
the `## Installed Agents` block on each run (regex stopped at the first `###`
|
||||||
|
subheading); rendering is now a shared, idempotent helper
|
||||||
|
- **Declared category honoured** — agent frontmatter `category` is authoritative
|
||||||
|
when valid (name/content heuristic is fallback only)
|
||||||
|
- **Installed-agent resolution** — `list_installed_agents` reads the frontmatter
|
||||||
|
name, so agents whose filename differs from their name resolve correctly
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **Renamed `agent-project-management.md` → `agent-project-assistant.md`** to
|
||||||
|
satisfy the `agent-<name>.md` convention (frontmatter `name: project-assistant`);
|
||||||
|
eliminates a registry name/filename collision
|
||||||
|
|
||||||
|
## [1.3.0] - 2026-06-17
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Scheduled agent execution (WP-0006, ADR-005)** — run agents on a cadence
|
||||||
|
against a preselected repo roster, orchestrated by activity-core and prepared
|
||||||
|
by kaizen-agentic (no Temporal workers or LLM runtime in this repo)
|
||||||
|
- **`kaizen-agentic schedule`** CLI group — `init`, `validate`, `list`,
|
||||||
|
`prepare <agent>` (markdown/json) over `.kaizen/schedule.yml`
|
||||||
|
- **`.kaizen/schedule.yml`** manifest + schema docs and example
|
||||||
|
(`docs/integrations/schedule-schema.md`, `docs/examples/.kaizen/schedule.yml`)
|
||||||
|
- **ActivityDefinition drafts** (`enabled: false`) — `weekly-coach-orientation`,
|
||||||
|
`weekly-optimization-review`
|
||||||
|
- **Design specs** — `discover_kaizen_scheduled_repos` resolver, State Hub
|
||||||
|
roster fields, `kaizen.schedule.prepared` event payload, activity-core handoff
|
||||||
|
checklist
|
||||||
|
|
||||||
## [1.2.0] - 2026-06-16
|
## [1.2.0] - 2026-06-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
56
CLAUDE.md
56
CLAUDE.md
@@ -1,5 +1,6 @@
|
|||||||
# kaizen-agentic — Claude Code Instructions
|
# kaizen-agentic — Claude Code Instructions
|
||||||
|
|
||||||
|
@SCOPE.md
|
||||||
@.claude/rules/repo-identity.md
|
@.claude/rules/repo-identity.md
|
||||||
@.claude/rules/session-protocol.md
|
@.claude/rules/session-protocol.md
|
||||||
@.claude/rules/first-session.md
|
@.claude/rules/first-session.md
|
||||||
@@ -7,56 +8,5 @@
|
|||||||
@.claude/rules/stack-and-commands.md
|
@.claude/rules/stack-and-commands.md
|
||||||
@.claude/rules/architecture.md
|
@.claude/rules/architecture.md
|
||||||
@.claude/rules/repo-boundary.md
|
@.claude/rules/repo-boundary.md
|
||||||
|
@.claude/rules/credential-routing.md
|
||||||
## Installed Agents
|
@.claude/rules/agents.md
|
||||||
|
|
||||||
This project includes the following specialized agents:
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
- **tdd-workflow**: Expert guidance for the TDD8 workflow methodology, specializing in the comprehensive ISSUE-TEST-RED-GREEN-REFACTOR-DOCUMENT-REFINE-PUBLISH cycle with sophisticated sidequest management and proper test organization.
|
|
||||||
|
|
||||||
Use these agents by referencing them in your Claude Code interactions.
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
- **claude-documentation**: Specialized assistant for Claude and Claude Code documentation, features, and best practices
|
|
||||||
|
|
||||||
### Meta
|
|
||||||
|
|
||||||
- **coach**: Coaching meta-agent that reads all agent memories in a project and synthesises cross-agent briefs and new-agent orientations
|
|
||||||
|
|
||||||
### Code Quality
|
|
||||||
|
|
||||||
- **code-refactoring**: Analyze code structure and quality, identify improvement opportunities, and provide actionable refactoring guidance. Use PROACTIVELY for code quality assessment and improvement.
|
|
||||||
- **datamodel-optimization**: Specialized agent that systematically analyzes, optimizes, and enhances dataclasses, models, and data structures within a codebase. Provides comprehensive datamodel improvements including convenience methods, interface consistency, code reduction, and test alignment.
|
|
||||||
- **optimization**: Meta-agent that analyzes and optimizes other Claude Code subagents based on their performance data, usage patterns, and effectiveness metrics. Use PROACTIVELY for agent ecosystem improvement.
|
|
||||||
- **tooling-optimization**: Meta-agent that analyzes and optimizes repository tooling usage to improve development efficiency
|
|
||||||
|
|
||||||
### Project Management
|
|
||||||
|
|
||||||
- **keepaChangelog**: Specialized assistant for maintaining CHANGELOG.md files following Keep a Changelog format
|
|
||||||
- **keepaContributingfile**: Specialized assistant for maintaining CONTRIBUTING.md files following Keep a Contributing-File V0.0.1 format within the Kaizen Agentic framework
|
|
||||||
- **keepaTodofile**: Specialized assistant for maintaining TODO.md files following Keep a Todofile V0.0.1 format
|
|
||||||
|
|
||||||
### Development Process
|
|
||||||
|
|
||||||
- **priority-evaluation**: Specialized assistant to help evaluate and establish priorities for issues and tasks.
|
|
||||||
- **releaseManager**: Manages software releases, version control, and publication workflows for Python packages
|
|
||||||
- **requirements-engineering**: Specialized agent designed to prevent interface compatibility issues and mock object mismatches by ensuring solid foundation planning before implementation. Based on lessons learned from Issue
|
|
||||||
- **scope-analyst**: Analyze a repository and produce/improve SCOPE.md for rapid orientation
|
|
||||||
- **wisdom-encouragement**: Provides encouraging wisdom and guidance for complex implementation tasks and challenging technical work
|
|
||||||
|
|
||||||
### Infrastructure
|
|
||||||
|
|
||||||
- **setupRepository**: Specialized assistant for setting up new Python repositories following PythonVibes best practices
|
|
||||||
- **sys-medic**: Linux/Kubernetes node health assessment agent — diagnoses process, memory, CPU, disk, network, and kubelet issues with safe, prioritized, evidence-driven guidance
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
- **tdd-workflow**: Expert guidance for the TDD8 workflow methodology, specializing in the comprehensive ISSUE-TEST-RED-GREEN-REFACTOR-DOCUMENT-REFINE-PUBLISH cycle with sophisticated sidequest management and proper test organization.
|
|
||||||
- **test-maintenance**: Specialized agent for analyzing and fixing failing tests in the project
|
|
||||||
- **testing-efficiency**: Specialized agent designed to optimize TDD8 workflow test execution, resolve pytest reliability issues, and enhance overall testing efficiency for red-green iterations. Focuses on smart test selection, parallel execution, and agent integration patterns.
|
|
||||||
|
|
||||||
Use these agents by referencing them in your Claude Code interactions.
|
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ This repository follows PythonVibes best practices:
|
|||||||
- `src/kaizen_agentic/` - Core framework source code
|
- `src/kaizen_agentic/` - Core framework source code
|
||||||
- `agents/` - Specialized agent definitions (17+ agents)
|
- `agents/` - Specialized agent definitions (17+ agents)
|
||||||
- `tests/` - Comprehensive test suite
|
- `tests/` - Comprehensive test suite
|
||||||
- `TODO.md` - Current development tasks (Keep a Todofile format)
|
- `workplans/` - Active workstreams and tasks (ADR-001)
|
||||||
- `CHANGELOG.md` - Version history (Keep a Changelog format)
|
- `CHANGELOG.md` - Version history (Keep a Changelog format)
|
||||||
|
|
||||||
### Making Changes
|
### Making Changes
|
||||||
@@ -155,15 +155,15 @@ When reporting bugs, please include:
|
|||||||
1. **Discuss significant changes** in an issue first
|
1. **Discuss significant changes** in an issue first
|
||||||
2. **Keep PRs focused** on a single feature or fix
|
2. **Keep PRs focused** on a single feature or fix
|
||||||
3. **Write clear commit messages** following conventional commit format
|
3. **Write clear commit messages** following conventional commit format
|
||||||
4. **Update relevant documentation** including TODO.md and CHANGELOG.md
|
4. **Update relevant documentation** including workplans and CHANGELOG.md
|
||||||
5. **Ensure all checks pass** including tests and linting
|
5. **Ensure all checks pass** including tests and linting
|
||||||
6. **Respond to review feedback** promptly and constructively
|
6. **Respond to review feedback** promptly and constructively
|
||||||
|
|
||||||
## Agent-Assisted Development
|
## Agent-Assisted Development
|
||||||
|
|
||||||
This repository includes 17+ specialized agents to assist with development:
|
This repository includes 17+ specialized agents to assist with development:
|
||||||
- Use `todo-keeper` for TODO.md maintenance
|
- Use `keepaChangelog` for CHANGELOG.md updates
|
||||||
- Use `changelog-keeper` for CHANGELOG.md updates
|
- Use `project-assistant` for workplan and session orientation
|
||||||
- Use `contributing-keeper` for this file maintenance
|
- Use `contributing-keeper` for this file maintenance
|
||||||
- See CLAUDE.md for complete agent catalog and usage
|
- See CLAUDE.md for complete agent catalog and usage
|
||||||
|
|
||||||
|
|||||||
2
SCOPE.md
2
SCOPE.md
@@ -74,7 +74,7 @@ This repo is the canonical home for the **KaizenAgentic** operating model (`INTE
|
|||||||
- Implementation: 20 agents, full CLI (`metrics`, `memory`, `feedback`), agency memory + ADR-004 metrics + optimizer wiring
|
- Implementation: 20 agents, full CLI (`metrics`, `memory`, `feedback`), agency memory + ADR-004 metrics + optimizer wiring
|
||||||
- Stability: CLI stable (Click workaround in place); Gitea CI on main; publish workflow on `v*` tags
|
- Stability: CLI stable (Click workaround in place); Gitea CI on main; publish workflow on `v*` tags
|
||||||
- Usage: internal dev projects and Custodian MCP hub-wide; pip install via Gitea extra index
|
- Usage: internal dev projects and Custodian MCP hub-wide; pip install via Gitea extra index
|
||||||
- Active work: **WP-0005** (adoption polish, fleet parity, publish verification → v1.2.0)
|
- Active work: **WP-0006** (scheduled agent execution via activity-core → v1.3.0)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
213
TODO.md
213
TODO.md
@@ -1,213 +0,0 @@
|
|||||||
# Todofile
|
|
||||||
|
|
||||||
This is a "to do next" file, particularly useful to keep the human and a coding assistant in sync.
|
|
||||||
|
|
||||||
The format is based on [Keep a Todofile V0.0.1](https://coulomb.social/open/TodoFileGuide).
|
|
||||||
|
|
||||||
The structure organizes **future tasks** by their impact, just as a changelog organizes past changes by their impact.
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## [Unreleased] - *Active Vibe-Coding State* 💡
|
|
||||||
|
|
||||||
Tasks in workplan: `workplans/kaizen-agentic-WP-0005-adoption-parity.md` (v1.2.0)
|
|
||||||
|
|
||||||
### To Add
|
|
||||||
* **Gitea publish pipeline verification** — secrets + workflow smoke test
|
|
||||||
* **`make agents-sync-package`** — keep `data/agents/` aligned with `agents/`
|
|
||||||
* **Install doc sweep** — HELLO_WORLD, CLI_CHEAT_SHEET, AGENT_DISTRIBUTION
|
|
||||||
|
|
||||||
### To Refactor
|
|
||||||
* **CI lint gate** — flake8 on `src/` in Gitea Actions
|
|
||||||
* **Makefile install hints** — point at Gitea registry or dev install
|
|
||||||
|
|
||||||
### Deferred to WP-0006 (v1.3.0)
|
|
||||||
* Interactive agent selection wizard
|
|
||||||
* Agent template schema validation in `validate`
|
|
||||||
* Documentation generation from agent metadata
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## [1.1.0] - Community Engagement — *Shipped 2026-06-18*
|
|
||||||
|
|
||||||
See `CHANGELOG.md` [1.1.0] and `workplans/kaizen-agentic-WP-0001-community-engagement.md`.
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## [COMPLETED] - *Documentation Standards Compliance - Version 0.2.0*
|
|
||||||
|
|
||||||
### ✅ Completed: To Add
|
|
||||||
* **CLI interface** for agent management and execution - DONE
|
|
||||||
- Full `kaizen-agentic` CLI with 8 commands (init, install, update, remove, list, status, validate, templates)
|
|
||||||
- Console script entry point for global availability
|
|
||||||
- Complete command-line interface with options and help
|
|
||||||
* **Comprehensive tests** for optimization module (PerformanceMetrics, OptimizationLoop) - DONE
|
|
||||||
- 24 tests covering all components with 100% pass rate
|
|
||||||
- Test coverage for registry, installer, and core functionality
|
|
||||||
- Automated test execution with make test/test-all targets
|
|
||||||
* **Agent registry and installer functionality** - DONE
|
|
||||||
- Complete AgentRegistry with categorization and dependency resolution
|
|
||||||
- AgentInstaller with backup/rollback capabilities
|
|
||||||
- ProjectInitializer with template-based project creation
|
|
||||||
* **Comprehensive documentation system** - DONE
|
|
||||||
- GETTING_STARTED.md - Complete setup and usage guide
|
|
||||||
- AGENT_DISTRIBUTION.md - Architecture documentation
|
|
||||||
- CLI_CHEAT_SHEET.md - Quick reference guide
|
|
||||||
- Updated README.md with complete usage examples
|
|
||||||
|
|
||||||
### ✅ Completed: To Refactor
|
|
||||||
* **Agent definitions** to follow consistent format standards - DONE
|
|
||||||
- All 17+ agents updated to YAML frontmatter format
|
|
||||||
- Consistent categorization system (project-management, development-process, etc.)
|
|
||||||
- Proper dependency declarations and descriptions
|
|
||||||
* **Makefile structure** for better user experience - DONE
|
|
||||||
- Reorganized with consistent naming (agents-, setup-, standards- prefixes)
|
|
||||||
- Enhanced help system with examples and clear descriptions
|
|
||||||
- 50+ targets organized into logical sections
|
|
||||||
* **Test execution performance** optimization - DONE
|
|
||||||
- Fast execution with all 24 tests completing in <1 second
|
|
||||||
- Parallel execution capabilities
|
|
||||||
- Efficient backup/cleanup in test teardown
|
|
||||||
|
|
||||||
### ✅ Completed: To Fix
|
|
||||||
* **Makefile targets** compatibility across environments - DONE
|
|
||||||
- Cross-platform compatibility with proper shell detection
|
|
||||||
- Virtual environment handling with fallback mechanisms
|
|
||||||
- Error handling and recovery in setup processes
|
|
||||||
* **Linting issues** in core modules - DONE
|
|
||||||
- All flake8 violations resolved across entire codebase
|
|
||||||
- PEP 8 compliance achieved
|
|
||||||
- Consistent code formatting with black
|
|
||||||
* **Virtual environment setup** reliability - DONE
|
|
||||||
- Robust venv creation and activation
|
|
||||||
- Dependency installation with upgrade handling
|
|
||||||
- Status checking and validation
|
|
||||||
* **CLI installation make target (agents-install-cli)** - DONE
|
|
||||||
- Target is working properly and requires virtual environment activation as documented
|
|
||||||
- Installation process functions correctly with proper venv setup
|
|
||||||
* **YAML frontmatter errors in agent files** causing loading failures - DONE
|
|
||||||
- All 16 agents now properly loading without YAML parsing errors
|
|
||||||
- Frontmatter format standardized across all agent definitions
|
|
||||||
* **Agent categorization issues** (agents showing as "Unknown" instead of proper categories) - DONE
|
|
||||||
- All 16 agents now properly categorized and displaying correct categories
|
|
||||||
- Category mapping and recognition system functioning correctly
|
|
||||||
|
|
||||||
### ✅ Completed: To Secure
|
|
||||||
* **Configuration file validation** - DONE
|
|
||||||
- Safe YAML parsing with error handling
|
|
||||||
- Input validation for all CLI parameters
|
|
||||||
- Backup creation before modifications
|
|
||||||
* **Agent integrity checks** - DONE
|
|
||||||
- Validation system for agent definitions
|
|
||||||
- Dependency conflict detection
|
|
||||||
- Installation verification
|
|
||||||
|
|
||||||
### ✅ Completed: To Remove
|
|
||||||
* **Obsolete test fixtures** - DONE
|
|
||||||
- Clean test directory structure
|
|
||||||
- Removed unused imports and variables
|
|
||||||
- Eliminated development scaffolding
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## [COMPLETED] - *Scenario 1: Greenfield Project Excellence - Version 0.2.1*
|
|
||||||
|
|
||||||
### ✅ Completed: Scenario 1 Tasks
|
|
||||||
* **Scenario 1 exploration: Establish codebase from scratch** - DONE
|
|
||||||
- Research current onboarding experience with existing setup targets - DONE
|
|
||||||
- Identify gaps in documentation for new project creation - DONE
|
|
||||||
- Test and validate smooth project initialization workflows - DONE
|
|
||||||
- Evaluate agent selection and recommendation systems for new projects - DONE
|
|
||||||
- Document best practices for greenfield project setup - DONE
|
|
||||||
|
|
||||||
### ✅ Completed: Additional Scenario 1 Improvements
|
|
||||||
* **Agent template name mappings fixed** - DONE
|
|
||||||
- Fixed agent template name mappings to match actual agent names
|
|
||||||
- Ensured consistency between template references and installed agents
|
|
||||||
* **ProjectInitializer enhancements** - DONE
|
|
||||||
- Added Makefile creation to ProjectInitializer for complete greenfield setup
|
|
||||||
- Implemented comprehensive project scaffolding
|
|
||||||
* **Test framework stability** - DONE
|
|
||||||
- Fixed all failing tests for agent framework updates
|
|
||||||
- Achieved 100% test pass rate across all components
|
|
||||||
* **Documentation and tutorial creation** - DONE
|
|
||||||
- Updated documentation with correct agent names
|
|
||||||
- Created HelloWorld tutorial for new users
|
|
||||||
- Comprehensive onboarding experience established
|
|
||||||
|
|
||||||
### ✅ Completed: Production Readiness
|
|
||||||
* **Scenario 1 state achievement** - DONE
|
|
||||||
- Scenario 1 (establish codebase from scratch) is now excellent and production-ready
|
|
||||||
- Complete end-to-end workflow for greenfield projects
|
|
||||||
- Robust agent selection and project initialization
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## [COMPLETED] - *Production Release with Release Management - Version 1.0.0*
|
|
||||||
|
|
||||||
### ✅ Completed: Release Management System
|
|
||||||
* **Complete release management system** with agent-releaseManager - DONE
|
|
||||||
- 6 structured make targets for complete release workflow
|
|
||||||
- `release-check` - Validate release readiness with comprehensive checklist
|
|
||||||
- `release-prepare` - Build packages and prepare for publication
|
|
||||||
- `release-test` - Test publication workflow using TestPyPI
|
|
||||||
- `release-publish` - Publish to production PyPI with safety checks
|
|
||||||
- `release-finalize` - Post-release tasks (tags, GitHub releases, documentation)
|
|
||||||
- `release-rollback` - Emergency rollback procedures and guidance
|
|
||||||
* **Local package installation capability** - DONE
|
|
||||||
- `make install-local` target for PyPI-equivalent testing
|
|
||||||
- Local package building and installation workflow
|
|
||||||
- Integration testing with locally built packages
|
|
||||||
* **Documentation updates for installation options** - DONE
|
|
||||||
- Updated documentation to reflect all installation methods
|
|
||||||
- PyPI installation guidance and local development setup
|
|
||||||
- Complete user onboarding documentation
|
|
||||||
* **Package distribution readiness** - DONE
|
|
||||||
- Full package ready for PyPI publication
|
|
||||||
- All agents included in package data distribution
|
|
||||||
- Console script entry point for global CLI availability
|
|
||||||
- Version 1.0.0 production release achieved
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## [COMPLETED] - *Scenario 2: Existing Project Integration Excellence - Version 0.2.2*
|
|
||||||
|
|
||||||
### ✅ Completed: Scenario 2 Tasks
|
|
||||||
* **Scenario 2 exploration: Integration with existing projects having agents** - DONE
|
|
||||||
- Research detection of existing agent systems in projects - DONE
|
|
||||||
- Design conflict resolution strategies for overlapping agent functionality - DONE
|
|
||||||
- Create migration paths for replacing outdated project-specific agents - DONE
|
|
||||||
- Develop extension mechanisms for project-specific kaizen agent customizations - DONE
|
|
||||||
- Define integration patterns that respect existing project structure - DONE
|
|
||||||
- Build tooling for safe agent system transitions - DONE
|
|
||||||
|
|
||||||
### ✅ Completed: Major Components Built
|
|
||||||
* **Detection system (detection.py)** for analyzing existing agent systems - DONE
|
|
||||||
- Comprehensive agent system detection across multiple formats
|
|
||||||
- Analysis of conflicts and overlap identification
|
|
||||||
- Integration readiness assessment
|
|
||||||
* **Migration framework (migration.py)** with 5 migration strategies - DONE
|
|
||||||
- Replace strategy for complete agent replacement
|
|
||||||
- Merge strategy for combining functionalities
|
|
||||||
- Extend strategy for adding new capabilities
|
|
||||||
- Coexist strategy for parallel operation
|
|
||||||
- Archive strategy for deprecation handling
|
|
||||||
* **Extension system (extensions.py)** for project-specific customizations - DONE
|
|
||||||
- Plugin-based architecture for agent customization
|
|
||||||
- Dynamic loading and configuration management
|
|
||||||
- Safe extension isolation and validation
|
|
||||||
* **Complete CLI integration** with detect, migrate, and extensions commands - DONE
|
|
||||||
- kaizen-agentic detect command for system analysis
|
|
||||||
- kaizen-agentic migrate command with strategy selection
|
|
||||||
- kaizen-agentic extensions command for customization management
|
|
||||||
* **Integration patterns documentation** with 5 proven scenarios - DONE
|
|
||||||
- Established patterns for common integration scenarios
|
|
||||||
- Best practices for safe agent system transitions
|
|
||||||
- Comprehensive documentation and examples
|
|
||||||
|
|
||||||
### ✅ Completed: Production Readiness
|
|
||||||
* **Scenario 2 state achievement** - DONE
|
|
||||||
- Scenario 2 (existing project integration) is now excellent and production-ready
|
|
||||||
- Complete detection, migration, and extension capabilities
|
|
||||||
- Safe and reliable agent system transitions
|
|
||||||
- Both Scenario 1 (greenfield) and Scenario 2 (existing projects) are production-ready
|
|
||||||
@@ -52,9 +52,28 @@ kaizen-agentic remove old-agent-name
|
|||||||
|
|
||||||
# Project status
|
# Project status
|
||||||
kaizen-agentic status # Show current project status
|
kaizen-agentic status # Show current project status
|
||||||
kaizen-agentic validate # Validate agent installation
|
kaizen-agentic validate # Validate agents (incl. frontmatter schema)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Authoring & Docs (WP-0007)
|
||||||
|
```bash
|
||||||
|
# Scaffold a new schema-valid agent (agents/agent-<name>.md)
|
||||||
|
kaizen-agentic create-agent my-agent -c testing -d "What it does"
|
||||||
|
kaizen-agentic create-agent my-agent # prompts for category + description
|
||||||
|
kaizen-agentic create-agent my-agent --force # overwrite existing
|
||||||
|
|
||||||
|
# Refresh the CLAUDE.md "Installed Agents" section from agent metadata
|
||||||
|
kaizen-agentic docs generate # idempotent rewrite
|
||||||
|
kaizen-agentic docs generate --check # CI gate: non-zero if out of date
|
||||||
|
|
||||||
|
# validate enforces required name/description/category, valid category,
|
||||||
|
# and valid memory/model values
|
||||||
|
kaizen-agentic validate
|
||||||
|
```
|
||||||
|
|
||||||
|
After adding or editing agents, run `make agents-sync-package` so the packaged
|
||||||
|
`data/agents/` copies stay in parity (release-check gate).
|
||||||
|
|
||||||
### Project Metrics (ADR-004)
|
### Project Metrics (ADR-004)
|
||||||
```bash
|
```bash
|
||||||
# Record outcome at session close
|
# Record outcome at session close
|
||||||
@@ -88,6 +107,24 @@ kaizen-agentic memory init tdd-workflow --no-metrics # memory only
|
|||||||
|
|
||||||
Session-close template: `docs/templates/session-close-protocol.md`
|
Session-close template: `docs/templates/session-close-protocol.md`
|
||||||
|
|
||||||
|
### Scheduled Agent Execution (ADR-005)
|
||||||
|
```bash
|
||||||
|
# Opt this repo into fleet scheduling
|
||||||
|
kaizen-agentic schedule init # coach + optimization weekly
|
||||||
|
kaizen-agentic schedule init --timezone UTC # override timezone
|
||||||
|
kaizen-agentic schedule init --engagement coulomb-loop \
|
||||||
|
--agents coach,optimization --bootstrap-cadence hourly
|
||||||
|
kaizen-agentic schedule validate # schema + agent-name checks
|
||||||
|
kaizen-agentic schedule list # enabled entries (--all incl. disabled)
|
||||||
|
|
||||||
|
# Prepare orientation for a scheduled run (offline; no State Hub needed)
|
||||||
|
kaizen-agentic schedule prepare coach # markdown bundle
|
||||||
|
kaizen-agentic schedule prepare optimization --format json
|
||||||
|
```
|
||||||
|
|
||||||
|
activity-core fires the schedule and creates a task per (repo, agent); the task
|
||||||
|
runs `schedule prepare`. kaizen-agentic does not run cron or invoke Claude.
|
||||||
|
|
||||||
### Information
|
### Information
|
||||||
```bash
|
```bash
|
||||||
# List templates
|
# List templates
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ invoke kaizen-agentic CLI commands.
|
|||||||
|------------|---------|-------------|
|
|------------|---------|-------------|
|
||||||
| [weekly-metrics-optimize](integrations/activity-definitions/weekly-metrics-optimize.md) | Cron Mon 08:00 | `metrics optimize` |
|
| [weekly-metrics-optimize](integrations/activity-definitions/weekly-metrics-optimize.md) | Cron Mon 08:00 | `metrics optimize` |
|
||||||
| [post-install-metrics-scaffold](integrations/activity-definitions/post-install-metrics-scaffold.md) | `kaizen.agent.installed` | `memory init` validation |
|
| [post-install-metrics-scaffold](integrations/activity-definitions/post-install-metrics-scaffold.md) | `kaizen.agent.installed` | `memory init` validation |
|
||||||
| [low-success-rate-review](integrations/activity-definitions/low-success-rate-review.md) | `kaizen.metrics.recorded` | `metrics show` + `optimize` |
|
| [low-success-rate-review](integrations/activity-definitions/low-success-rate-review.md) | `kaizen.metrics.recorded` | `metrics record --emit-event` |
|
||||||
|
|
||||||
**Activation handoff (activity-core owners):**
|
**Activation handoff (activity-core owners):**
|
||||||
|
|
||||||
@@ -73,6 +73,45 @@ test -f .kaizen/metrics/optimizer/analysis.json && echo OK
|
|||||||
|
|
||||||
**Boundary:** kaizen-agentic does not run Temporal schedules.
|
**Boundary:** kaizen-agentic does not run Temporal schedules.
|
||||||
|
|
||||||
|
### Scheduled agent execution (WP-0006, ADR-005)
|
||||||
|
|
||||||
|
Beyond the metrics-only definitions above, agents themselves run on a cadence
|
||||||
|
against a **preselected repo roster**. The roster combines three sources:
|
||||||
|
|
||||||
|
| Source | Purpose |
|
||||||
|
|--------|---------|
|
||||||
|
| State Hub `GET /repos/` | Canonical slug list + `host_paths` |
|
||||||
|
| Repo opt-in: `.kaizen/schedule.yml` exists with `version` set | Per-repo enablement |
|
||||||
|
| Optional hub flag `kaizen_schedule_enabled: true` (future) | Operator override |
|
||||||
|
|
||||||
|
A repo is **schedule-eligible** when it is registered with reachable
|
||||||
|
`host_paths` **and** carries a valid `.kaizen/schedule.yml`. The activity-core
|
||||||
|
resolver `discover_kaizen_scheduled_repos` intersects these and emits
|
||||||
|
`context.scheduled_runs` (one entry per `(repo, agent)`); definitions `for_each`
|
||||||
|
over that output.
|
||||||
|
|
||||||
|
| Definition | Trigger | Agent | Prepare command |
|
||||||
|
|------------|---------|-------|-----------------|
|
||||||
|
| [weekly-coach-orientation](integrations/activity-definitions/weekly-coach-orientation.md) | Cron Mon 09:00 | `coach` | `schedule prepare coach` |
|
||||||
|
| [weekly-optimization-review](integrations/activity-definitions/weekly-optimization-review.md) | Cron Mon 10:00 | `optimization` | `schedule prepare optimization` |
|
||||||
|
|
||||||
|
**Listing schedule-eligible repos** (operator, no activity-core code):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In each candidate repo on a host listed in state-hub host_paths:
|
||||||
|
kaizen-agentic schedule validate && kaizen-agentic schedule list
|
||||||
|
```
|
||||||
|
|
||||||
|
**Design docs (no state-hub / activity-core code in this repo):**
|
||||||
|
|
||||||
|
- [schedule-schema.md](integrations/schedule-schema.md) — `.kaizen/schedule.yml`
|
||||||
|
- [state-hub-roster-fields.md](integrations/state-hub-roster-fields.md) — hub fields/filters
|
||||||
|
- [discover-kaizen-scheduled-repos.md](integrations/discover-kaizen-scheduled-repos.md) — resolver spec
|
||||||
|
- [kaizen-schedule-prepared-event.md](integrations/kaizen-schedule-prepared-event.md) — event payload
|
||||||
|
|
||||||
|
**Boundary:** kaizen-agentic declares and prepares; activity-core schedules;
|
||||||
|
state-hub owns the roster.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Pattern 3 — artifact-store evidence retention
|
## Pattern 3 — artifact-store evidence retention
|
||||||
|
|||||||
@@ -128,3 +128,27 @@ token with `TWINE_USERNAME=__token__`):
|
|||||||
make release-publish
|
make release-publish
|
||||||
python -m twine upload dist/*
|
python -m twine upload dist/*
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Scheduled-run runner prerequisites (WP-0006)
|
||||||
|
|
||||||
|
A runner that executes a scheduled kaizen agent task (fired by activity-core)
|
||||||
|
needs:
|
||||||
|
|
||||||
|
- **`kaizen-agentic` on PATH** — `pip install kaizen-agentic` (or `pipx install
|
||||||
|
kaizen-agentic`) using the Gitea PyPI extra index when installing from the
|
||||||
|
internal registry:
|
||||||
|
```bash
|
||||||
|
pip install kaizen-agentic \
|
||||||
|
--extra-index-url https://gitea.coulomb.social/api/packages/coulomb/pypi/simple/
|
||||||
|
```
|
||||||
|
- **Repo checkout reachable** at the `host_paths[<host>]` registered in State
|
||||||
|
Hub, with a valid `.kaizen/schedule.yml` (`kaizen-agentic schedule validate`).
|
||||||
|
- **No State Hub required for `prepare`** — `schedule prepare` reads local
|
||||||
|
`.kaizen/` state only. The hub is needed by the *resolver* (activity-core),
|
||||||
|
not by the prepared session.
|
||||||
|
|
||||||
|
**Enabling a definition** (activity-core operator): keep the kaizen definitions
|
||||||
|
at `enabled: false` until a manual smoke test passes (see
|
||||||
|
[INTEGRATION_PATTERNS.md Pattern 2](INTEGRATION_PATTERNS.md) and the
|
||||||
|
[activity-core handoff checklist](integrations/activity-core-handoff-wp0006.md)),
|
||||||
|
then flip one definition to `enabled: true` in staging before fleet-wide enable.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Memory-enabled agents record per-session outcomes at close:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
kaizen-agentic metrics record <agent> --success --time <s> --quality <0-1>
|
kaizen-agentic metrics record <agent> --success --time <s> --quality <0-1>
|
||||||
|
kaizen-agentic metrics record <agent> --success --time <s> --quality <0-1> --emit-event
|
||||||
kaizen-agentic metrics optimize [agent]
|
kaizen-agentic metrics optimize [agent]
|
||||||
kaizen-agentic memory brief <agent> # includes Performance Summary
|
kaizen-agentic memory brief <agent> # includes Performance Summary
|
||||||
```
|
```
|
||||||
@@ -45,4 +46,4 @@ fleet layers above satisfy INTENT's "measurable agents" requirement without tele
|
|||||||
## Feedback loop
|
## Feedback loop
|
||||||
|
|
||||||
User experience feedback uses [FEEDBACK.md](FEEDBACK.md) and Gitea issue templates —
|
User experience feedback uses [FEEDBACK.md](FEEDBACK.md) and Gitea issue templates —
|
||||||
separate from execution metrics.
|
separate from execution metrics.
|
||||||
|
|||||||
150
docs/adr/ADR-005-scheduled-agent-execution.md
Normal file
150
docs/adr/ADR-005-scheduled-agent-execution.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
---
|
||||||
|
id: ADR-005
|
||||||
|
title: Scheduled Agent Execution Convention
|
||||||
|
status: accepted
|
||||||
|
date: "2026-06-17"
|
||||||
|
---
|
||||||
|
|
||||||
|
# ADR-005 — Scheduled Agent Execution Convention
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Kaizen agents are markdown instruction sets invoked in coding-agent sessions.
|
||||||
|
ADR-004 added project-scoped metrics; WP-0004 committed three metrics-focused
|
||||||
|
`ActivityDefinition` drafts (`enabled: false`). What is still missing is a way to
|
||||||
|
run **agents themselves** — not just the metrics optimizer — on a **regular
|
||||||
|
cadence** against a **preselected set of repos**, without kaizen-agentic owning
|
||||||
|
Temporal workers, cron, or an LLM runtime.
|
||||||
|
|
||||||
|
The ecosystem already separates concerns:
|
||||||
|
|
||||||
|
- **activity-core** owns scheduling (cron/event → task creation).
|
||||||
|
- **state-hub** owns the canonical repo roster and `host_paths`.
|
||||||
|
- **kaizen-agentic** owns the agents, project memory, and metrics.
|
||||||
|
|
||||||
|
A scheduled agent run therefore needs a contract that crosses these repos
|
||||||
|
without merging them.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Introduce a **repo-local schedule manifest** and a **prepare** step. The
|
||||||
|
end-to-end flow:
|
||||||
|
|
||||||
|
```
|
||||||
|
activity-core cron
|
||||||
|
→ context resolver (roster ∩ repos with schedule.yml)
|
||||||
|
→ task per (repo, agent)
|
||||||
|
→ coding-agent session runs `kaizen-agentic schedule prepare <agent>`
|
||||||
|
→ session executes the agent instructions in that repo
|
||||||
|
```
|
||||||
|
|
||||||
|
kaizen-agentic's responsibilities are exactly two: **declare** the schedule
|
||||||
|
(`.kaizen/schedule.yml`) and **prepare** an orientation bundle for a run. It
|
||||||
|
does **not** fire cron, create tasks, or invoke Claude.
|
||||||
|
|
||||||
|
### 1. Schedule manifest — `.kaizen/schedule.yml`
|
||||||
|
|
||||||
|
A repo opts into fleet scheduling by committing this file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "1"
|
||||||
|
timezone: Europe/Berlin
|
||||||
|
agents:
|
||||||
|
coach:
|
||||||
|
cadence: weekly
|
||||||
|
cron: "0 9 * * 1" # optional override; default from ActivityDefinition
|
||||||
|
enabled: true
|
||||||
|
optimization:
|
||||||
|
cadence: weekly
|
||||||
|
cron: "0 10 * * 1"
|
||||||
|
enabled: true
|
||||||
|
tdd-workflow:
|
||||||
|
cadence: monthly
|
||||||
|
enabled: false
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schema:**
|
||||||
|
|
||||||
|
| Key | Required | Type | Notes |
|
||||||
|
|-----|----------|------|-------|
|
||||||
|
| `version` | yes | string | Must be `"1"` |
|
||||||
|
| `timezone` | no | string | IANA tz; default supplied by ActivityDefinition |
|
||||||
|
| `agents` | yes | mapping | `agent-name → settings` |
|
||||||
|
| `agents.<name>.cadence` | yes | enum | `daily` \| `weekly` \| `monthly` |
|
||||||
|
| `agents.<name>.cron` | no | string | 5-field cron; overrides cadence default |
|
||||||
|
| `agents.<name>.enabled` | no | bool | Default `true` |
|
||||||
|
|
||||||
|
**Validation rules** (`kaizen-agentic schedule validate`):
|
||||||
|
|
||||||
|
- `version` must equal `"1"`.
|
||||||
|
- Every agent key must be an installed or packaged agent name.
|
||||||
|
- `cadence` must be one of the allowed values.
|
||||||
|
- Duplicate agent entries are rejected.
|
||||||
|
|
||||||
|
### 2. Roster (preselected repos)
|
||||||
|
|
||||||
|
A repo is **schedule-eligible** when **all** of:
|
||||||
|
|
||||||
|
1. It is a registered repo in state-hub (`GET /repos/`) with reachable
|
||||||
|
`host_paths`.
|
||||||
|
2. It contains a valid `.kaizen/schedule.yml`.
|
||||||
|
3. (Optional, future) it carries a `kaizen_schedule_enabled: true` hub flag.
|
||||||
|
|
||||||
|
The resolver `discover_kaizen_scheduled_repos` (specified in
|
||||||
|
`docs/integrations/discover-kaizen-scheduled-repos.md`, implemented in
|
||||||
|
activity-core) intersects these sources and emits `context.scheduled_runs`.
|
||||||
|
|
||||||
|
### 3. Prepare bundle — `schedule prepare <agent>`
|
||||||
|
|
||||||
|
Assembles, from **local `.kaizen/` state only** (offline-safe):
|
||||||
|
|
||||||
|
- The agent prompt (`agents/agent-<name>.md`, installed or packaged).
|
||||||
|
- Project memory (`.kaizen/agents/<name>/memory.md`) when present.
|
||||||
|
- Metrics summary (`.kaizen/metrics/<name>/summary.json`) when present.
|
||||||
|
- Repo pointers (`SCOPE.md`, `workplans/`) when present.
|
||||||
|
- Suggested session-close commands (`metrics record`, memory update).
|
||||||
|
|
||||||
|
Output is `markdown` (default) or `json` (`--format json`) so activity-core can
|
||||||
|
embed it in a task `description` or a runner can parse it.
|
||||||
|
|
||||||
|
### CLI interface
|
||||||
|
|
||||||
|
```
|
||||||
|
kaizen-agentic schedule init [--target PATH] [--timezone TZ] [--force]
|
||||||
|
kaizen-agentic schedule init --engagement <slug> [--agents A,B] [--bootstrap-cadence hourly|daily|weekly]
|
||||||
|
kaizen-agentic schedule validate [--target PATH]
|
||||||
|
kaizen-agentic schedule list [--target PATH] [--all]
|
||||||
|
kaizen-agentic schedule prepare <agent> [--target PATH] [--format markdown|json]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- **No scheduling code** in kaizen-agentic. Cron and task creation belong to
|
||||||
|
activity-core; the roster query belongs to state-hub.
|
||||||
|
- **No LLM invocation.** `prepare` produces a runner-agnostic bundle; a human or
|
||||||
|
automated coding-agent session executes it.
|
||||||
|
- **State-hub schema changes** (roster opt-in flag) are designed here but
|
||||||
|
implemented in `the-custodian` (repo boundary).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Operators declare per-repo schedules and a fleet roster without tribal
|
||||||
|
knowledge.
|
||||||
|
- activity-core can fire recurring tasks referencing `schedule prepare`.
|
||||||
|
- A scheduled session opens with full orientation (prompt + memory + metrics).
|
||||||
|
- The existing `weekly-metrics-optimize` definition (ADR-004 / WP-0004) remains
|
||||||
|
complementary; an `optimization` agent run may chain `schedule prepare
|
||||||
|
optimization` then `metrics optimize`.
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- [ADR-002: Project Memory Convention](ADR-002-project-memory-convention.md)
|
||||||
|
- [ADR-004: Project Metrics Convention](ADR-004-project-metrics-convention.md)
|
||||||
|
- [docs/integrations/schedule-schema.md](../integrations/schedule-schema.md)
|
||||||
|
- [docs/integrations/discover-kaizen-scheduled-repos.md](../integrations/discover-kaizen-scheduled-repos.md)
|
||||||
|
- [docs/agency-framework.md](../agency-framework.md)
|
||||||
|
- [KAIZEN-WP-0006](../../workplans/kaizen-agentic-WP-0006-scheduled-agent-execution.md)
|
||||||
74
docs/adr/ADR-006-customer-engagement-convention.md
Normal file
74
docs/adr/ADR-006-customer-engagement-convention.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# ADR-006: Customer Engagement Convention
|
||||||
|
|
||||||
|
**Status:** accepted
|
||||||
|
**Date:** 2026-06-18
|
||||||
|
**Deciders:** kaizen-agentic maintainers (supplier)
|
||||||
|
**Customer reference:** coulomb-loop / LOOP-WP-0000
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Coulomb's `coulomb-loop` engagement introduced a **customer/supplier split**:
|
||||||
|
the customer repo holds contracts and operations; `kaizen-agentic` ships agents
|
||||||
|
and CLI; target repos hold `.kaizen/` runtime state. We need a durable convention
|
||||||
|
so the second customer engagement reuses ≤50% of coulomb-loop setup effort.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
### 1. Repo roles
|
||||||
|
|
||||||
|
| Role | Owns |
|
||||||
|
|------|------|
|
||||||
|
| **Customer engagement repo** | INTENT, workplans, ADRs, rosters, ActivityDefinition copies, loop health |
|
||||||
|
| **Supplier (kaizen-agentic)** | Agent prompts, CLI, integration specs, playbook, supplier workplans |
|
||||||
|
| **Target repos** | `.kaizen/schedule.yml`, `agents/`, `metrics/`, memory |
|
||||||
|
| **activity-core** | Cron/event orchestration, resolvers, task creation |
|
||||||
|
|
||||||
|
### 2. `.kaizen/` placement
|
||||||
|
|
||||||
|
Project-scoped kaizen state **never** lives in the customer engagement repo.
|
||||||
|
It lives only in repos where agents execute (fleet pilots).
|
||||||
|
|
||||||
|
### 3. Supplier CLI engagement mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule init --engagement <customer-slug> \
|
||||||
|
--agents coach,optimization --bootstrap-cadence hourly
|
||||||
|
```
|
||||||
|
|
||||||
|
Writes bootstrap schedule with engagement comment and hourly cron presets.
|
||||||
|
See [customer-engagement-playbook.md](../integrations/customer-engagement-playbook.md).
|
||||||
|
|
||||||
|
### 4. Event emission (opt-in)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic metrics record <agent> --success --time <s> --quality <0-1> --emit-event
|
||||||
|
```
|
||||||
|
|
||||||
|
Publishes `kaizen.metrics.recorded` for activity-core event definitions.
|
||||||
|
Default off for backward compatibility.
|
||||||
|
|
||||||
|
### 5. Playbook lifecycle
|
||||||
|
|
||||||
|
1. **Bootstrap** — customer LOOP-WP-0000; supplier KAIZEN-WP-0008 Part 1
|
||||||
|
2. **Smoke** — hourly E2E on pilot roster (LOOP-WP-0001)
|
||||||
|
3. **Automate** — emit-event, activity-core definitions enabled incrementally
|
||||||
|
4. **Generalize** — supplier-notes → playbook v1; ADR-006 + layout docs
|
||||||
|
5. **Second customer** — copy layout; swap roster and definitions
|
||||||
|
|
||||||
|
### 6. ActivityDefinition ownership
|
||||||
|
|
||||||
|
Bootstrap uses **customer-owned copies** (coulomb DEC-003 option A).
|
||||||
|
Hybrid override manifest (option C) is design-only — see
|
||||||
|
[activity-definition-override-manifest.md](../integrations/activity-definition-override-manifest.md).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- New engagements follow [customer-engagement-repo-layout.md](../integrations/customer-engagement-repo-layout.md)
|
||||||
|
- Supplier friction is logged in customer `loops/*/supplier-notes.md` and absorbed into playbook
|
||||||
|
- activity-core handoff requirements are documented per engagement wave
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- coulomb-loop `docs/adr/ADR-002-customer-supplier-boundary.md`
|
||||||
|
- [ADR-005](ADR-005-scheduled-agent-execution.md) — schedule contract
|
||||||
|
- [KAIZEN-WP-0008](../../workplans/kaizen-agentic-WP-0008-coulomb-loop-supplier-engagement.md)
|
||||||
@@ -80,6 +80,23 @@ memory: enabled # or: disabled
|
|||||||
|
|
||||||
The `memory` field defaults to `enabled`. Set `memory: disabled` for agents that are stateless by design (e.g. `wisdom-encouragement`).
|
The `memory` field defaults to `enabled`. Set `memory: disabled` for agents that are stateless by design (e.g. `wisdom-encouragement`).
|
||||||
|
|
||||||
|
The file name must follow `agent-<name>.md` where `<name>` equals the frontmatter
|
||||||
|
`name`. `kaizen-agentic validate` enforces the frontmatter schema (required
|
||||||
|
fields, known `category`, valid `memory`/`model`).
|
||||||
|
|
||||||
|
### Authoring and doc sync (WP-0007)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic create-agent <name> -c <category> -d "<description>"
|
||||||
|
kaizen-agentic validate # schema + dependency checks
|
||||||
|
kaizen-agentic docs generate # refresh CLAUDE.md Installed Agents
|
||||||
|
make agents-sync-package # keep packaged data/agents/ in parity
|
||||||
|
```
|
||||||
|
|
||||||
|
`create-agent` writes a schema-valid skeleton; `docs generate` rewrites the
|
||||||
|
project `## Installed Agents` section **idempotently** (use `--check` as a CI
|
||||||
|
gate). The section is grouped by the declared frontmatter `category`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## The Coach Meta-Agent
|
## The Coach Meta-Agent
|
||||||
@@ -299,12 +316,35 @@ Manifest: [integrations/optimizer-artifact-manifest.md](integrations/optimizer-a
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Scheduled Agent Execution
|
||||||
|
|
||||||
|
Agents can run on a **regular cadence** against **preselected repos**, fired by
|
||||||
|
**activity-core** and prepared by kaizen-agentic (ADR-005). A repo opts in by
|
||||||
|
committing `.kaizen/schedule.yml`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule init # scaffold (coach + optimization weekly)
|
||||||
|
kaizen-agentic schedule validate # check schema + agent names
|
||||||
|
kaizen-agentic schedule list # show enabled entries
|
||||||
|
kaizen-agentic schedule prepare coach # orientation bundle for a scheduled run
|
||||||
|
```
|
||||||
|
|
||||||
|
`schedule prepare <agent>` bundles the agent prompt, project memory, metrics
|
||||||
|
summary, and repo pointers — offline, no State Hub required. kaizen-agentic does
|
||||||
|
**not** run cron or invoke Claude; activity-core fires the schedule and a
|
||||||
|
coding-agent session executes the prepared bundle.
|
||||||
|
|
||||||
|
Schema: [integrations/schedule-schema.md](integrations/schedule-schema.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Related Documents
|
## Related Documents
|
||||||
|
|
||||||
- [ADR-001: Workplan Convention](adr/ADR-001-workplan-convention.md)
|
- [ADR-001: Workplan Convention](adr/ADR-001-workplan-convention.md)
|
||||||
- [ADR-002: Project Memory Convention](adr/ADR-002-project-memory-convention.md)
|
- [ADR-002: Project Memory Convention](adr/ADR-002-project-memory-convention.md)
|
||||||
- [ADR-003: Protocols Artifact Convention](adr/ADR-003-protocols-artifact-convention.md)
|
- [ADR-003: Protocols Artifact Convention](adr/ADR-003-protocols-artifact-convention.md)
|
||||||
- [ADR-004: Project Metrics Convention](adr/ADR-004-project-metrics-convention.md)
|
- [ADR-004: Project Metrics Convention](adr/ADR-004-project-metrics-convention.md)
|
||||||
|
- [ADR-005: Scheduled Agent Execution](adr/ADR-005-scheduled-agent-execution.md)
|
||||||
- [wiki/EcosystemIntegration.md](../wiki/EcosystemIntegration.md) — two-layer measurement model
|
- [wiki/EcosystemIntegration.md](../wiki/EcosystemIntegration.md) — two-layer measurement model
|
||||||
- [WP-0002: Agency Framework](../workplans/kaizen-agentic-WP-0002-agency-framework.md)
|
- [WP-0002: Agency Framework](../workplans/kaizen-agentic-WP-0002-agency-framework.md)
|
||||||
- [WP-0003: Measurement Loop](../workplans/kaizen-agentic-WP-0003-measurement-loop.md)
|
- [WP-0003: Measurement Loop](../workplans/kaizen-agentic-WP-0003-measurement-loop.md)
|
||||||
|
|||||||
17
docs/examples/.kaizen/schedule.yml
Normal file
17
docs/examples/.kaizen/schedule.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Kaizen scheduled agent execution manifest (ADR-005)
|
||||||
|
# Declares which agents run on what cadence in this repo.
|
||||||
|
# Validate with: kaizen-agentic schedule validate
|
||||||
|
version: "1"
|
||||||
|
timezone: Europe/Berlin
|
||||||
|
agents:
|
||||||
|
coach:
|
||||||
|
cadence: weekly
|
||||||
|
cron: "0 9 * * 1" # Monday 09:00 — cross-agent orientation brief
|
||||||
|
enabled: true
|
||||||
|
optimization:
|
||||||
|
cadence: weekly
|
||||||
|
cron: "0 10 * * 1" # Monday 10:00 — agent performance review
|
||||||
|
enabled: true
|
||||||
|
tdd-workflow:
|
||||||
|
cadence: monthly
|
||||||
|
enabled: false # declared but paused; operator opts in deliberately
|
||||||
109
docs/integrations/activity-core-handoff-engagement.md
Normal file
109
docs/integrations/activity-core-handoff-engagement.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# activity-core Handoff — Customer Engagement Bootstrap (WP-0008)
|
||||||
|
|
||||||
|
Coordination requirements for **activity-core** to support coulomb-loop-style
|
||||||
|
customer engagements after kaizen-agentic ships `schedule init --engagement` and
|
||||||
|
`metrics record --emit-event`.
|
||||||
|
|
||||||
|
Open as an activity-core issue titled *"Engagement bootstrap: event-payload +
|
||||||
|
bootstrap cadence alignment"*.
|
||||||
|
|
||||||
|
## Supplier capabilities (kaizen-agentic — done)
|
||||||
|
|
||||||
|
- [x] `schedule init --engagement <slug> --agents coach,optimization --bootstrap-cadence hourly`
|
||||||
|
- [x] `metrics record --emit-event` → `activity.kaizen.metrics.recorded`
|
||||||
|
- [x] Resolver contract: [discover-kaizen-scheduled-repos.md](discover-kaizen-scheduled-repos.md)
|
||||||
|
|
||||||
|
## activity-core requirements
|
||||||
|
|
||||||
|
### R1 — `event-payload` context resolver (blocks LOOP-WP-0002 event path)
|
||||||
|
|
||||||
|
**Problem:** Event-triggered definitions bind `context.metrics` via
|
||||||
|
`type: event-payload`, but `resolve_context` treats unknown types as `{}`.
|
||||||
|
|
||||||
|
**Requirement:** Register a resolver (or inline handler) that, when
|
||||||
|
`event_envelope_json` is present, binds `EventEnvelope.attributes` to the
|
||||||
|
`bind_to` key (stripping `context.` prefix).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
context_sources:
|
||||||
|
- type: event-payload
|
||||||
|
bind_to: context.metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected snapshot after resolve:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{"metrics": {"agent": "coach", "project": "kaizen-agentic", "summary": {...}}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance:** Manual NATS publish of a [kaizen-metrics-recorded-event](kaizen-metrics-recorded-event.md)
|
||||||
|
envelope triggers `low-success-rate-review` and evaluates
|
||||||
|
`context.metrics.summary.success_rate` without binding `{}`.
|
||||||
|
|
||||||
|
### R2 — Bootstrap cadence enum vs cron (blocks mis-tuned filters)
|
||||||
|
|
||||||
|
**Problem:** Engagement bootstrap writes **hourly cron** expressions but keeps
|
||||||
|
`cadence: daily` in `.kaizen/schedule.yml` (coulomb-loop ADR-003 convention).
|
||||||
|
Customer ActivityDefinitions filter `discover_kaizen_scheduled_repos` with
|
||||||
|
`cadence: daily`.
|
||||||
|
|
||||||
|
**Requirement:** Do **not** require `cadence: hourly` in schedule.yml (invalid
|
||||||
|
in ADR-005 schema). Continue matching on the `cadence` enum field; rely on
|
||||||
|
per-repo `cron` overrides for hourly firing.
|
||||||
|
|
||||||
|
**Acceptance:** Resolver with `cadence: daily` returns pilot repos whose
|
||||||
|
`schedule.yml` contains `cron: "15 * * * *"` / `"30 * * * *"`.
|
||||||
|
|
||||||
|
### R3 — ActivityDefinition cron alignment
|
||||||
|
|
||||||
|
Customer coulomb definitions offset from repo schedule crons:
|
||||||
|
|
||||||
|
| Definition | activity-core cron | Repo schedule cron |
|
||||||
|
|------------|-------------------|-------------------|
|
||||||
|
| hourly-metrics-optimize | `0 * * * *` | (metrics path; no schedule) |
|
||||||
|
| hourly-coach-orientation | `15 * * * *` | coach `15 * * * *` |
|
||||||
|
| hourly-optimization-review | `30 * * * *` | optimization `30 * * * *` |
|
||||||
|
|
||||||
|
**Requirement:** Keep definition crons and repo schedule crons in sync during
|
||||||
|
bootstrap. Document in customer handoff that `schedule init --engagement`
|
||||||
|
presets must match enabled ActivityDefinition offsets.
|
||||||
|
|
||||||
|
### R4 — Event router registration
|
||||||
|
|
||||||
|
**Requirement:** Ensure `kaizen.metrics.recorded` is an allowed event type in
|
||||||
|
`event_type_registry` (if registry is enforced on publish path).
|
||||||
|
|
||||||
|
**Acceptance:** Event router dispatches to enabled `low-success-rate-review`
|
||||||
|
definitions when envelope `type` matches.
|
||||||
|
|
||||||
|
### R5 — Engagement roster path (optional v1.1)
|
||||||
|
|
||||||
|
Customer rosters live in the engagement repo (e.g.
|
||||||
|
`coulomb-loop/loops/kaizen-stack/roster.yaml`), not in target repos.
|
||||||
|
|
||||||
|
**Suggestion:** Support optional resolver param `engagement_slug` that looks up
|
||||||
|
roster path from a hub field or env `KAIZEN_ENGAGEMENT_ROSTER` so definitions
|
||||||
|
need not hard-code absolute paths.
|
||||||
|
|
||||||
|
## Smoke sequence (end-to-end)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Target repo (supplier runs on pilot)
|
||||||
|
kaizen-agentic schedule init --engagement coulomb-loop \
|
||||||
|
--agents coach,optimization --bootstrap-cadence hourly --force
|
||||||
|
kaizen-agentic schedule validate
|
||||||
|
kaizen-agentic memory init coach && kaizen-agentic memory init optimization
|
||||||
|
|
||||||
|
# Record + emit (requires nats-py)
|
||||||
|
kaizen-agentic metrics record coach --success --time 60 --quality 0.9 --emit-event
|
||||||
|
|
||||||
|
# activity-core
|
||||||
|
# 1. Resolver dry-run: discover_kaizen_scheduled_repos + cadence=daily → 6 runs
|
||||||
|
# 2. Event dry-run: publish sample envelope → low-success-rate-review tasks
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [customer engagement playbook](customer-engagement-playbook.md) (supplier)
|
||||||
|
- coulomb-loop `docs/integrations/activity-core-handoff.md` (customer)
|
||||||
|
- [KAIZEN-WP-0008](../../workplans/kaizen-agentic-WP-0008-coulomb-loop-supplier-engagement.md)
|
||||||
82
docs/integrations/activity-core-handoff-wp0006.md
Normal file
82
docs/integrations/activity-core-handoff-wp0006.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# activity-core Handoff — Scheduled Agent Execution (WP-0006)
|
||||||
|
|
||||||
|
Coordination checklist for the **activity-core** team to enable kaizen scheduled
|
||||||
|
agent runs. kaizen-agentic owns the schedule contract, the prepare CLI, and the
|
||||||
|
ActivityDefinition **drafts**; activity-core owns the resolver, the schedule
|
||||||
|
firing, and task creation (repo boundary, ADR-005).
|
||||||
|
|
||||||
|
Open this as an activity-core issue/PR titled *"Enable kaizen scheduled agent
|
||||||
|
execution (WP-0006)"* and track the boxes there.
|
||||||
|
|
||||||
|
## What kaizen-agentic ships (done in this repo)
|
||||||
|
|
||||||
|
- [x] `.kaizen/schedule.yml` schema + `schedule validate|init|list|prepare` CLI
|
||||||
|
- [x] ADR-005 contract
|
||||||
|
- [x] Resolver spec: [discover-kaizen-scheduled-repos.md](discover-kaizen-scheduled-repos.md)
|
||||||
|
- [x] State Hub roster fields design: [state-hub-roster-fields.md](state-hub-roster-fields.md)
|
||||||
|
- [x] Draft definitions (`enabled: false`):
|
||||||
|
[weekly-coach-orientation](activity-definitions/weekly-coach-orientation.md),
|
||||||
|
[weekly-optimization-review](activity-definitions/weekly-optimization-review.md)
|
||||||
|
- [x] Event payload spec: [kaizen-schedule-prepared-event.md](kaizen-schedule-prepared-event.md)
|
||||||
|
|
||||||
|
## What activity-core must do
|
||||||
|
|
||||||
|
- [x] **Implement resolver** `discover_kaizen_scheduled_repos` per the spec
|
||||||
|
(`activity_core/context_resolvers/kaizen.py`).
|
||||||
|
- [x] **Add resolver unit tests** — `tests/test_kaizen_context_resolver.py` (7 passed).
|
||||||
|
- [ ] **Copy custodian weekly definitions** from `docs/integrations/activity-definitions/`
|
||||||
|
(`weekly-coach-orientation.md`, `weekly-optimization-review.md`) into the
|
||||||
|
activity-core catalog path (per ACT-ADR-002). *Deferred: coulomb-loop engagement
|
||||||
|
ships customer-owned daily bootstrap definitions instead (LOOP-WP-0001).*
|
||||||
|
- [ ] **Register** weekly definition slugs in the activity-core index (when custodian
|
||||||
|
fleet moves off coulomb engagement copies).
|
||||||
|
- [x] **Run** `make sync-activity-definitions` — coulomb-loop definitions synced
|
||||||
|
(`coulomb-hourly-coach-orientation`, `coulomb-hourly-optimization-review`, etc.).
|
||||||
|
- [x] **Wire cron** triggers — daily stabilize chain on pilot roster (08:00–10:00).
|
||||||
|
- [x] **Smoke test** — resolver dry-run returns 6 `scheduled_runs` on coulomb roster;
|
||||||
|
`schedule prepare coach` succeeds on `kaizen-agentic` and `the-custodian`.
|
||||||
|
- [x] **Enable gradually** — coulomb bootstrap definitions `enabled: true` on pilot.
|
||||||
|
- [x] **Verify runner prerequisites** — `kaizen-agentic` on PATH; `KAIZEN_RUNNER_HOST`
|
||||||
|
set on worker (`bnt-lap001`).
|
||||||
|
|
||||||
|
### Supplier closure evidence (2026-06-18)
|
||||||
|
|
||||||
|
| Check | Result |
|
||||||
|
|-------|--------|
|
||||||
|
| Resolver dry-run (coulomb roster, `cadence: daily`) | 6 runs across 3 pilot repos |
|
||||||
|
| `tests/test_kaizen_context_resolver.py` | 7 passed |
|
||||||
|
| `schedule validate` + `prepare coach` | OK on `kaizen-agentic`, `the-custodian`, `activity-core` |
|
||||||
|
| Live definitions using resolver | `f234bb1a` coach, `097fc027` optimization (enabled) |
|
||||||
|
|
||||||
|
**Remaining activity-core item:** optional custodian-fleet weekly templates (`enabled: false`
|
||||||
|
in kaizen-agentic drafts) when engagement moves beyond coulomb-loop bootstrap copies.
|
||||||
|
|
||||||
|
## State Hub team (the-custodian)
|
||||||
|
|
||||||
|
- [ ] Optional: add `kaizen_schedule_enabled` repo flag + `GET /repos/` filter
|
||||||
|
(v2 pre-filter; the repo file remains the source of truth).
|
||||||
|
|
||||||
|
## Smoke test (manual, runner-agnostic)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/pilot-repo
|
||||||
|
kaizen-agentic schedule init # if not already present
|
||||||
|
kaizen-agentic schedule validate # exit 0
|
||||||
|
kaizen-agentic schedule list # shows coach + optimization enabled
|
||||||
|
kaizen-agentic schedule prepare coach # non-empty orientation bundle
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in activity-core: run the resolver (dry-run) and confirm one
|
||||||
|
`scheduled_run` per enabled `(repo, agent)` with a correct `prepare_command`.
|
||||||
|
|
||||||
|
## Pilot roster
|
||||||
|
|
||||||
|
- `kaizen-agentic` (dogfood)
|
||||||
|
- `the-custodian` (hub operator)
|
||||||
|
- one additional custodian-domain repo with `.kaizen/` state (TBD at pilot time)
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [ADR-005](../adr/ADR-005-scheduled-agent-execution.md)
|
||||||
|
- [INTEGRATION_PATTERNS.md Pattern 2](../INTEGRATION_PATTERNS.md)
|
||||||
|
- [KAIZEN-WP-0006](../../workplans/kaizen-agentic-WP-0006-scheduled-agent-execution.md)
|
||||||
72
docs/integrations/activity-definition-override-manifest.md
Normal file
72
docs/integrations/activity-definition-override-manifest.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# ActivityDefinition Override Manifest (design)
|
||||||
|
|
||||||
|
**Status:** design-only (DEC-003 option C future)
|
||||||
|
**Implements:** KAIZEN-WP-0008 T08
|
||||||
|
**Runtime:** none in v1 — customer-owned copies (option A) during bootstrap
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Customer repos copy supplier ActivityDefinitions and tune cron, labels, and
|
||||||
|
`enabled` flags. When supplier templates change, manual merge is error-prone.
|
||||||
|
A manifest declares **source + overrides** without duplicating full definition bodies.
|
||||||
|
|
||||||
|
## Proposed file
|
||||||
|
|
||||||
|
`activity-definitions/manifest.yaml` in the customer repo:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "1"
|
||||||
|
supplier_repo: kaizen-agentic
|
||||||
|
supplier_path: docs/integrations/activity-definitions
|
||||||
|
engagement: coulomb-loop
|
||||||
|
|
||||||
|
definitions:
|
||||||
|
- supplier_id: kaizen-weekly-coach-orientation
|
||||||
|
customer_id: coulomb-hourly-coach-orientation
|
||||||
|
source: ../../kaizen-agentic/docs/integrations/activity-definitions/weekly-coach-orientation.md
|
||||||
|
overrides:
|
||||||
|
id: coulomb-hourly-coach-orientation
|
||||||
|
name: Hourly Kaizen Coach Orientation (coulomb-loop bootstrap)
|
||||||
|
owner: coulomb-loop
|
||||||
|
governance: coulomb_social
|
||||||
|
enabled: true
|
||||||
|
trigger:
|
||||||
|
cron_expression: "15 * * * *"
|
||||||
|
context_sources:
|
||||||
|
- params:
|
||||||
|
roster: /home/worsch/coulomb-loop/loops/kaizen-stack/roster.yaml
|
||||||
|
cadence: daily
|
||||||
|
|
||||||
|
- supplier_id: kaizen-low-success-rate-review
|
||||||
|
customer_id: coulomb-low-success-rate-review
|
||||||
|
overrides:
|
||||||
|
enabled: false
|
||||||
|
owner: coulomb-loop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Merge rules (future tooling)
|
||||||
|
|
||||||
|
1. Parse supplier definition markdown (frontmatter + body)
|
||||||
|
2. Deep-merge `overrides` (customer wins on conflict)
|
||||||
|
3. Write customer copy to `activity-definitions/<customer_id>.md`
|
||||||
|
4. `sync_activity_definitions` reads customer copies only (unchanged today)
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
| Layer | Owner |
|
||||||
|
|-------|-------|
|
||||||
|
| Manifest schema | kaizen-agentic (supplier ADR/extension) |
|
||||||
|
| Manifest instance | customer repo |
|
||||||
|
| Merge CLI | TBD — `coulomb-loop` or `activity-core` |
|
||||||
|
| Sync runtime | activity-core (unchanged) |
|
||||||
|
|
||||||
|
## Bootstrap path (today)
|
||||||
|
|
||||||
|
Use **option A**: hand-copy definitions, edit cron/labels, track drift in
|
||||||
|
`loops/kaizen-stack/supplier-notes.md`. Revisit manifest when a second customer
|
||||||
|
engagement starts.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- coulomb-loop `docs/decisions/DEC-003-activity-definition-ownership.md`
|
||||||
|
- [customer-engagement-playbook.md](customer-engagement-playbook.md)
|
||||||
@@ -35,9 +35,11 @@ action:
|
|||||||
|
|
||||||
**Threshold:** 0.8 success rate, minimum 5 executions (avoids noise on early pilots).
|
**Threshold:** 0.8 success rate, minimum 5 executions (avoids noise on early pilots).
|
||||||
|
|
||||||
**CLI mapping:** Event emitter is future work; manual check today:
|
**CLI mapping:** `kaizen-agentic metrics record <agent> --emit-event` after each
|
||||||
|
append (see [kaizen-metrics-recorded-event.md](../kaizen-metrics-recorded-event.md)).
|
||||||
|
Manual check today:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kaizen-agentic metrics show <agent> # inspect summary.success_rate
|
kaizen-agentic metrics show <agent> # inspect summary.success_rate
|
||||||
kaizen-agentic metrics optimize <agent>
|
kaizen-agentic metrics optimize <agent>
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
id: kaizen-weekly-coach-orientation
|
||||||
|
name: Weekly Kaizen Coach Orientation
|
||||||
|
enabled: false
|
||||||
|
owner: kaizen-agentic
|
||||||
|
governance: custodian
|
||||||
|
status: proposed
|
||||||
|
trigger:
|
||||||
|
type: cron
|
||||||
|
cron_expression: "0 9 * * 1"
|
||||||
|
timezone: Europe/Berlin
|
||||||
|
misfire_policy: skip
|
||||||
|
context_sources:
|
||||||
|
- type: resolver
|
||||||
|
query: discover_kaizen_scheduled_repos
|
||||||
|
params:
|
||||||
|
domain: custodian
|
||||||
|
cadence: weekly
|
||||||
|
bind_to: context.scheduled_runs
|
||||||
|
---
|
||||||
|
|
||||||
|
# Weekly Kaizen Coach Orientation
|
||||||
|
|
||||||
|
Every Monday 09:00 Berlin time, opens a coach orientation task for each
|
||||||
|
schedule-eligible repo whose `.kaizen/schedule.yml` enables the `coach` agent.
|
||||||
|
|
||||||
|
The resolver `discover_kaizen_scheduled_repos` (see
|
||||||
|
[discover-kaizen-scheduled-repos.md](../discover-kaizen-scheduled-repos.md))
|
||||||
|
returns one `scheduled_run` per `(repo, agent)`; this definition selects the
|
||||||
|
`coach` runs.
|
||||||
|
|
||||||
|
```rule
|
||||||
|
id: run-weekly-coach
|
||||||
|
for_each: context.scheduled_runs
|
||||||
|
bind_as: r
|
||||||
|
condition: 'context.r.agent == "coach" and context.r.enabled'
|
||||||
|
action:
|
||||||
|
task_template: "Weekly coach orientation: {context.r.repo}"
|
||||||
|
description: |
|
||||||
|
{context.r.prepare_command}
|
||||||
|
Then load agents/agent-coach.md in a coding-agent session, paste the
|
||||||
|
prepared bundle, and follow the coach synthesis. At session close:
|
||||||
|
kaizen-agentic metrics record coach --success --time <s> --quality <0-1>
|
||||||
|
target_repo: context.r.repo
|
||||||
|
priority: medium
|
||||||
|
labels: ["kaizen", "agent-run", "coach", "scheduled", "automated"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**CLI mapping:** `kaizen-agentic schedule prepare coach` (offline-safe; reads
|
||||||
|
local `.kaizen/` state).
|
||||||
|
|
||||||
|
**Activation:** sync into activity-core via `make sync-activity-definitions`
|
||||||
|
after the `discover_kaizen_scheduled_repos` resolver is enabled. Keep
|
||||||
|
`enabled: false` until a manual smoke test passes on a pilot repo. See
|
||||||
|
[INTEGRATION_PATTERNS.md Pattern 2](../../INTEGRATION_PATTERNS.md).
|
||||||
@@ -27,13 +27,13 @@ Invokes the kaizen-agentic optimizer CLI per project.
|
|||||||
id: run-weekly-optimizer
|
id: run-weekly-optimizer
|
||||||
for_each: context.projects
|
for_each: context.projects
|
||||||
bind_as: p
|
bind_as: p
|
||||||
condition: 'p.has_metrics == true'
|
condition: 'context.p.has_metrics'
|
||||||
action:
|
action:
|
||||||
task_template: "Run kaizen metrics optimize on {{p.repo}}"
|
task_template: "Run kaizen metrics optimize on {context.p.repo}"
|
||||||
description: |
|
description: |
|
||||||
cd {{p.root}} && kaizen-agentic metrics optimize
|
cd {context.p.root} && kaizen-agentic metrics optimize
|
||||||
Optional: kaizen-agentic metrics publish (when artifact-store configured)
|
Optional: kaizen-agentic metrics publish (when artifact-store configured)
|
||||||
target_repo: "{{p.repo}}"
|
target_repo: context.p.repo
|
||||||
priority: medium
|
priority: medium
|
||||||
labels: ["kaizen", "metrics", "optimizer", "automated"]
|
labels: ["kaizen", "metrics", "optimizer", "automated"]
|
||||||
```
|
```
|
||||||
@@ -41,4 +41,4 @@ action:
|
|||||||
**Activation:** sync this definition into activity-core via `make sync-activity-definitions`
|
**Activation:** sync this definition into activity-core via `make sync-activity-definitions`
|
||||||
after enabling the shell resolver for `discover_kaizen_projects`.
|
after enabling the shell resolver for `discover_kaizen_projects`.
|
||||||
|
|
||||||
**CLI mapping:** `kaizen-agentic metrics optimize` (no agent filter = all agents with metrics).
|
**CLI mapping:** `kaizen-agentic metrics optimize` (no agent filter = all agents with metrics).
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
id: kaizen-weekly-optimization-review
|
||||||
|
name: Weekly Kaizen Optimization Review
|
||||||
|
enabled: false
|
||||||
|
owner: kaizen-agentic
|
||||||
|
governance: custodian
|
||||||
|
status: proposed
|
||||||
|
trigger:
|
||||||
|
type: cron
|
||||||
|
cron_expression: "0 10 * * 1"
|
||||||
|
timezone: Europe/Berlin
|
||||||
|
misfire_policy: skip
|
||||||
|
context_sources:
|
||||||
|
- type: resolver
|
||||||
|
query: discover_kaizen_scheduled_repos
|
||||||
|
params:
|
||||||
|
domain: custodian
|
||||||
|
cadence: weekly
|
||||||
|
bind_to: context.scheduled_runs
|
||||||
|
---
|
||||||
|
|
||||||
|
# Weekly Kaizen Optimization Review
|
||||||
|
|
||||||
|
Every Monday 10:00 Berlin time, opens an optimization-agent review task for each
|
||||||
|
schedule-eligible repo whose `.kaizen/schedule.yml` enables the `optimization`
|
||||||
|
agent. Chains the agent orientation with the existing metrics optimizer so the
|
||||||
|
review is evidence-backed.
|
||||||
|
|
||||||
|
```rule
|
||||||
|
id: run-weekly-optimization
|
||||||
|
for_each: context.scheduled_runs
|
||||||
|
bind_as: r
|
||||||
|
condition: 'context.r.agent == "optimization" and context.r.enabled'
|
||||||
|
action:
|
||||||
|
task_template: "Weekly optimization review: {context.r.repo}"
|
||||||
|
description: |
|
||||||
|
{context.r.prepare_command}
|
||||||
|
kaizen-agentic metrics optimize # refresh evidence
|
||||||
|
Then load agents/agent-optimization.md, paste the prepared bundle plus the
|
||||||
|
optimizer report, and act on recommendations. At session close:
|
||||||
|
kaizen-agentic metrics record optimization --success --time <s> --quality <0-1>
|
||||||
|
target_repo: context.r.repo
|
||||||
|
priority: medium
|
||||||
|
labels: ["kaizen", "agent-run", "optimization", "scheduled", "automated"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**CLI mapping:** `kaizen-agentic schedule prepare optimization` then
|
||||||
|
`kaizen-agentic metrics optimize`.
|
||||||
|
|
||||||
|
**Complementarity:** this generalizes the metrics-only
|
||||||
|
[weekly-metrics-optimize](weekly-metrics-optimize.md) definition into a full
|
||||||
|
agent run. Repos may run either; running both duplicates the optimizer step.
|
||||||
|
|
||||||
|
**Activation:** sync into activity-core via `make sync-activity-definitions`
|
||||||
|
after the resolver is enabled; hold at `enabled: false` until smoke-tested.
|
||||||
98
docs/integrations/customer-engagement-playbook.md
Normal file
98
docs/integrations/customer-engagement-playbook.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Customer Engagement Playbook v1 (supplier)
|
||||||
|
|
||||||
|
How kaizen-agentic supports a **customer engagement repo** (reference:
|
||||||
|
[coulomb-loop](https://gitea.coulomb.social/coulomb/coulomb-loop)). Second engagements
|
||||||
|
should copy [customer-engagement-repo-layout.md](customer-engagement-repo-layout.md)
|
||||||
|
and complete the checklist in one session.
|
||||||
|
|
||||||
|
## Roles
|
||||||
|
|
||||||
|
| Repo | Role |
|
||||||
|
|------|------|
|
||||||
|
| Customer (coulomb-loop) | Roster, ActivityDefinition copies, cadence policy, loop health |
|
||||||
|
| Supplier (kaizen-agentic) | Agents, CLI, integration contracts, this playbook |
|
||||||
|
| Target repos | `.kaizen/` state (schedule, memory, metrics) |
|
||||||
|
| activity-core | Cron + event orchestration, task creation |
|
||||||
|
|
||||||
|
## Bootstrap checklist
|
||||||
|
|
||||||
|
### 1. Customer repo
|
||||||
|
|
||||||
|
- Register engagement in state-hub (`register_project.sh`)
|
||||||
|
- Write `INTENT.md`, `SCOPE.md`, LOOP-WP-0000–0004 workplans
|
||||||
|
- Accept ADRs and DEC-* decisions; run `fix-consistency REPO=<customer>`
|
||||||
|
- Copy ActivityDefinitions to `activity-definitions/` (DEC-003 option A)
|
||||||
|
- Register reuse-surface capability (LOOP-WP-0000 T08)
|
||||||
|
- Enable definitions incrementally: metrics → coach → optimization
|
||||||
|
|
||||||
|
### 2. Target repos (per pilot)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule init --engagement <customer-slug> \
|
||||||
|
--agents coach,optimization --bootstrap-cadence hourly
|
||||||
|
kaizen-agentic schedule validate
|
||||||
|
kaizen-agentic memory init coach
|
||||||
|
kaizen-agentic memory init optimization
|
||||||
|
```
|
||||||
|
|
||||||
|
Hourly bootstrap uses `cadence: daily` with hourly `cron` overrides — see
|
||||||
|
[activity-core-handoff-engagement.md](activity-core-handoff-engagement.md) R2.
|
||||||
|
|
||||||
|
### 3. Session close (each agent run)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic metrics record <agent> --success --time <s> --quality <0-1>
|
||||||
|
kaizen-agentic metrics record <agent> --success --time <s> --quality <0-1> --emit-event
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires `pip install 'kaizen-agentic[events]'` for `--emit-event`.
|
||||||
|
|
||||||
|
### 4. activity-core
|
||||||
|
|
||||||
|
- Sync definitions from customer `activity-definitions/`
|
||||||
|
- Implement handoff requirements in [activity-core-handoff-engagement.md](activity-core-handoff-engagement.md)
|
||||||
|
- Manual trigger smoke before enabling hourly crons
|
||||||
|
|
||||||
|
## Lessons from coulomb-loop (supplier-notes absorbed)
|
||||||
|
|
||||||
|
| Observation | Resolution (shipped) |
|
||||||
|
|-------------|----------------------|
|
||||||
|
| `schedule init` weekly defaults unsuitable for bootstrap | `schedule init --engagement --bootstrap-cadence hourly` (KAIZEN-WP-0008 T04) |
|
||||||
|
| No metrics event for LOOP-WP-0002 | `metrics record --emit-event` (T03) |
|
||||||
|
| `metrics optimize` with 0 records confusing during bootstrap | Expected — document in bootstrap log; optimizer needs ≥10 records for recommendations |
|
||||||
|
| ActivityDefinition drift from supplier templates | Customer copies (DEC-003 A); hybrid manifest design for v2 ([override manifest](activity-definition-override-manifest.md)) |
|
||||||
|
| Rotation saturation signals | Future: `metrics rotation-signals` CLI (ADR-004 follow-on) |
|
||||||
|
|
||||||
|
## Cadence promotion
|
||||||
|
|
||||||
|
Customer regulator (LOOP-WP-0004) approves promotion. Use atomic promote to
|
||||||
|
align all three layers (cadence.yml, activity-definitions, fleet schedule.yml,
|
||||||
|
activity-core sync):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule promote \
|
||||||
|
--engagement-repo /path/to/customer-loop \
|
||||||
|
--engagement <slug> \
|
||||||
|
--to-phase operate \
|
||||||
|
--activity-core /path/to/activity-core
|
||||||
|
```
|
||||||
|
|
||||||
|
Dry-run: `--dry-run`. Repair fleet drift after a partial promotion: `--fleet-only`.
|
||||||
|
|
||||||
|
Legacy per-layer commands still work:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule init --engagement <slug> --bootstrap-cadence daily --force
|
||||||
|
kaizen-agentic schedule init --engagement <slug> --bootstrap-cadence weekly --force
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reference implementation
|
||||||
|
|
||||||
|
- Customer: [coulomb-loop INTENT](https://gitea.coulomb.social/coulomb/coulomb-loop/src/branch/main/INTENT.md)
|
||||||
|
- Supplier workplan: [KAIZEN-WP-0008](../../workplans/kaizen-agentic-WP-0008-coulomb-loop-supplier-engagement.md)
|
||||||
|
- Convention: [ADR-006](../adr/ADR-006-customer-engagement-convention.md)
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [INTEGRATION_PATTERNS.md](../INTEGRATION_PATTERNS.md) Pattern 2
|
||||||
|
- [activity-core-handoff-wp0006.md](activity-core-handoff-wp0006.md)
|
||||||
65
docs/integrations/customer-engagement-repo-layout.md
Normal file
65
docs/integrations/customer-engagement-repo-layout.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Customer Engagement Repo Layout
|
||||||
|
|
||||||
|
Contract for a **customer engagement repository** that orchestrates kaizen
|
||||||
|
improvement loops across a fleet roster. Reference implementation:
|
||||||
|
[coulomb-loop](https://gitea.coulomb.social/coulomb/coulomb-loop) (`coulomb_social` domain).
|
||||||
|
|
||||||
|
Supplier agents and CLI live in `kaizen-agentic`. Runtime `.kaizen/` state lives
|
||||||
|
in **target repos**, not in the customer repo.
|
||||||
|
|
||||||
|
## Directory tree
|
||||||
|
|
||||||
|
```
|
||||||
|
customer-repo/
|
||||||
|
├── INTENT.md # Engagement purpose, loop map, cadence policy summary
|
||||||
|
├── SCOPE.md # In/out of scope; supplier boundaries
|
||||||
|
├── CLAUDE.md # Session protocol + rules includes
|
||||||
|
├── .claude/rules/ # repo-identity, architecture, workplan-convention, …
|
||||||
|
├── workplans/
|
||||||
|
│ ├── <PREFIX>-WP-0000-* # Bootstrap (registration, ADRs, decisions)
|
||||||
|
│ ├── <PREFIX>-WP-0001-* # Primary improvement stack
|
||||||
|
│ ├── <PREFIX>-WP-0002-* # Reactive quality escalation
|
||||||
|
│ ├── <PREFIX>-WP-0003-* # Registry / orientation hygiene
|
||||||
|
│ └── <PREFIX>-WP-0004-* # Loop regulator (cadence promotion, rotation)
|
||||||
|
├── docs/
|
||||||
|
│ ├── adr/ # Customer ADRs (boundary, cadence, ownership)
|
||||||
|
│ ├── decisions/ # DEC-* proposals awaiting operator acceptance
|
||||||
|
│ └── integrations/ # activity-core handoff, event payloads
|
||||||
|
├── history/ # Assessments, milestone snapshots
|
||||||
|
├── activity-definitions/ # Customer-owned copies (DEC-003 option A)
|
||||||
|
├── loops/
|
||||||
|
│ └── <loop-id>/
|
||||||
|
│ ├── roster.yaml # Pilot + expansion_queue + saturated
|
||||||
|
│ ├── bootstrap-log.md # E2E cycle evidence
|
||||||
|
│ ├── supplier-notes.md # Friction fed back to kaizen-agentic
|
||||||
|
│ └── rotation-policy.yml # ADR-004 diminishing-returns (optional)
|
||||||
|
└── registry/ # reuse-surface capability index (engagement D0)
|
||||||
|
├── indexes/capabilities.yaml
|
||||||
|
└── capabilities/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Naming
|
||||||
|
|
||||||
|
| Artifact | coulomb-loop example |
|
||||||
|
|----------|----------------------|
|
||||||
|
| Workplan prefix | `LOOP-WP-NNNN` |
|
||||||
|
| Hub topic slug | `coulomb_social` |
|
||||||
|
| Engagement slug in schedules | `coulomb-loop` |
|
||||||
|
| Supplier workplan | `KAIZEN-WP-0008` (kaizen-agentic) |
|
||||||
|
|
||||||
|
## What does not live here
|
||||||
|
|
||||||
|
- `agents/agent-*.md` — supplier (`kaizen-agentic`)
|
||||||
|
- `.kaizen/schedule.yml` on customer repo — target fleet repos only
|
||||||
|
- Temporal workers / resolvers — `activity-core`
|
||||||
|
- state-hub service code — `the-custodian`
|
||||||
|
|
||||||
|
## Bootstrap sequence
|
||||||
|
|
||||||
|
See [customer-engagement-playbook.md](customer-engagement-playbook.md).
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- coulomb-loop `docs/adr/ADR-002-customer-supplier-boundary.md`
|
||||||
|
- [ADR-006](../adr/ADR-006-customer-engagement-convention.md)
|
||||||
|
- [KAIZEN-WP-0008](../../workplans/kaizen-agentic-WP-0008-coulomb-loop-supplier-engagement.md)
|
||||||
109
docs/integrations/discover-kaizen-scheduled-repos.md
Normal file
109
docs/integrations/discover-kaizen-scheduled-repos.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Resolver Spec: `discover_kaizen_scheduled_repos`
|
||||||
|
|
||||||
|
**Status:** implemented in activity-core (`context_resolvers/kaizen.py`). This doc
|
||||||
|
is the contract an activity-core implementer needs to add the context resolver
|
||||||
|
that feeds the scheduled-agent ActivityDefinitions (ADR-005).
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Given the fleet roster and per-repo schedule manifests, emit one entry per
|
||||||
|
`(repo, agent)` that is due to run, so ActivityDefinitions can `for_each` over
|
||||||
|
the result.
|
||||||
|
|
||||||
|
## Signature
|
||||||
|
|
||||||
|
```
|
||||||
|
discover_kaizen_scheduled_repos(
|
||||||
|
domain: str | None = None, # optional scope filter, e.g. "custodian"
|
||||||
|
cadence: str | None = None, # optional: "daily" | "weekly" | "monthly"
|
||||||
|
now: datetime | None = None, # injection point for testing
|
||||||
|
) -> { "scheduled_runs": list[ScheduledRun] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Bound in a definition as:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
context_sources:
|
||||||
|
- type: resolver
|
||||||
|
query: discover_kaizen_scheduled_repos
|
||||||
|
params:
|
||||||
|
domain: custodian
|
||||||
|
cadence: weekly
|
||||||
|
bind_to: context.scheduled_runs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inputs (sources, in order)
|
||||||
|
|
||||||
|
1. **State Hub** `GET /repos/` (optionally filtered by `domain` and, when the v2
|
||||||
|
flag lands, `kaizen_schedule_enabled=true`). Yields `slug`, `host_paths`,
|
||||||
|
`domain`.
|
||||||
|
2. **Repo checkout** at `host_paths[<runner-host>]`: read
|
||||||
|
`.kaizen/schedule.yml`. Skip repos without the file.
|
||||||
|
3. **Validation**: run the equivalent of `kaizen-agentic schedule validate`.
|
||||||
|
Skip (and log) repos whose schedule is invalid — never emit a bad entry.
|
||||||
|
|
||||||
|
## Output shape
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scheduled_runs": [
|
||||||
|
{
|
||||||
|
"repo": "kaizen-agentic",
|
||||||
|
"root": "/home/worsch/kaizen-agentic",
|
||||||
|
"agent": "coach",
|
||||||
|
"cadence": "weekly",
|
||||||
|
"cron": "0 9 * * 1",
|
||||||
|
"timezone": "Europe/Berlin",
|
||||||
|
"enabled": true,
|
||||||
|
"prepare_command": "kaizen-agentic schedule prepare coach --target /home/worsch/kaizen-agentic"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `ScheduledRun` fields
|
||||||
|
|
||||||
|
| Field | Source | Notes |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| `repo` | hub `slug` | becomes `target_repo` on the created task |
|
||||||
|
| `root` | `host_paths[<host>]` | absolute checkout path on the runner |
|
||||||
|
| `agent` | schedule.yml key | |
|
||||||
|
| `cadence` | schedule.yml | |
|
||||||
|
| `cron` | schedule.yml or definition default | per-repo override when present |
|
||||||
|
| `timezone` | schedule.yml or definition default | |
|
||||||
|
| `enabled` | schedule.yml (`true` only emitted) | disabled entries are filtered out |
|
||||||
|
| `prepare_command` | derived | exact CLI the task should run |
|
||||||
|
|
||||||
|
## Filtering rules
|
||||||
|
|
||||||
|
- Emit only entries with `enabled: true`.
|
||||||
|
- When `cadence` param is set, emit only matching entries (lets each cron-bound
|
||||||
|
definition select its own cadence slice).
|
||||||
|
- When `cron` is present on the entry, it is the authoritative per-repo time;
|
||||||
|
otherwise the definition's cron applies.
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Condition | Behavior |
|
||||||
|
|-----------|----------|
|
||||||
|
| Repo unreachable / path missing on host | Skip + log `repo_unreachable` |
|
||||||
|
| `.kaizen/schedule.yml` absent | Skip silently (not opted in) |
|
||||||
|
| schedule.yml invalid | Skip + log `schedule_invalid` with validation errors |
|
||||||
|
| Hub unreachable | Fail the resolver run (no roster = no safe output) |
|
||||||
|
|
||||||
|
The resolver must be **idempotent** and **side-effect free**: it reads, it does
|
||||||
|
not write. Task creation happens in the ActivityDefinition rule, not here.
|
||||||
|
|
||||||
|
## Test fixtures
|
||||||
|
|
||||||
|
- A repo with valid `.kaizen/schedule.yml` (coach enabled) → one entry.
|
||||||
|
- A repo with the file but coach `enabled: false` → no entry.
|
||||||
|
- A repo without the file → no entry.
|
||||||
|
- A repo with an invalid schedule → no entry + logged error.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [state-hub-roster-fields.md](state-hub-roster-fields.md)
|
||||||
|
- [schedule-schema.md](schedule-schema.md)
|
||||||
|
- [activity-definitions/weekly-coach-orientation.md](activity-definitions/weekly-coach-orientation.md)
|
||||||
|
- [ADR-005](../adr/ADR-005-scheduled-agent-execution.md)
|
||||||
73
docs/integrations/kaizen-metrics-recorded-event.md
Normal file
73
docs/integrations/kaizen-metrics-recorded-event.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Event Payload: `kaizen.metrics.recorded`
|
||||||
|
|
||||||
|
**Status:** implemented — `kaizen-agentic metrics record --emit-event`
|
||||||
|
|
||||||
|
**Registry:** `event-types/kaizen.metrics.recorded.md` (producer-owned, synced via
|
||||||
|
activity-core `make sync-event-types` with `ACTIVITY_DEFINITION_DIRS` including
|
||||||
|
this repo)
|
||||||
|
|
||||||
|
Emitted after a successful metrics append when `--emit-event` is set. Default
|
||||||
|
off for backward compatibility.
|
||||||
|
|
||||||
|
## Subject
|
||||||
|
|
||||||
|
```
|
||||||
|
activity.kaizen.metrics.recorded
|
||||||
|
```
|
||||||
|
|
||||||
|
Published to NATS (activity-core `EventEnvelope` format). Consumed by
|
||||||
|
activity-core event router and definitions such as `low-success-rate-review`.
|
||||||
|
|
||||||
|
## Envelope
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"type": "kaizen.metrics.recorded",
|
||||||
|
"version": "1.0",
|
||||||
|
"timestamp": "2026-06-18T12:00:00Z",
|
||||||
|
"publisher": "kaizen-agentic",
|
||||||
|
"attributes": {
|
||||||
|
"agent": "coach",
|
||||||
|
"project": "kaizen-agentic",
|
||||||
|
"summary": {
|
||||||
|
"success_rate": 0.75,
|
||||||
|
"execution_count": 12,
|
||||||
|
"avg_quality": 0.81
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Attribute fields
|
||||||
|
|
||||||
|
| Field | Type | Notes |
|
||||||
|
|-------|------|-------|
|
||||||
|
| `agent` | string | Agent name from `metrics record <agent>` |
|
||||||
|
| `project` | string | Repo slug — `KAIZEN_PROJECT_SLUG` env or directory basename |
|
||||||
|
| `summary.success_rate` | float | Rolling rate from `summary.json` after append |
|
||||||
|
| `summary.execution_count` | int | Total executions |
|
||||||
|
| `summary.avg_quality` | float | Maps from `avg_quality_score` in ADR-004 summary |
|
||||||
|
|
||||||
|
## CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic metrics record coach --success --time 120 --quality 0.9 --emit-event
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires `nats-py` (`pip install 'kaizen-agentic[events]'`). Configure broker via
|
||||||
|
`NATS_URL` (default `nats://localhost:4222`).
|
||||||
|
|
||||||
|
Events are **not** emitted when append is skipped (duplicate idempotency key).
|
||||||
|
|
||||||
|
## Consumers
|
||||||
|
|
||||||
|
- **activity-core** — `trigger.type: event` with `event_type: kaizen.metrics.recorded`
|
||||||
|
- **coulomb-loop** — `low-success-rate-review` (LOOP-WP-0002); replaces hourly
|
||||||
|
health sweep when event path is stable
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [low-success-rate-review](activity-definitions/low-success-rate-review.md)
|
||||||
|
- [INTEGRATION_PATTERNS.md Pattern 2](../INTEGRATION_PATTERNS.md)
|
||||||
|
- coulomb-loop `loops/quality-escalation/event-payload.md` (customer contract)
|
||||||
90
docs/integrations/kaizen-schedule-prepared-event.md
Normal file
90
docs/integrations/kaizen-schedule-prepared-event.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# Event Payload: `kaizen.schedule.prepared`
|
||||||
|
|
||||||
|
**Status:** design — for **future event-driven runs**. v1 of WP-0006 is
|
||||||
|
cron-driven (activity-core fires the schedule). This event lets a runner or
|
||||||
|
activity-core react when a `schedule prepare` bundle has been assembled, without
|
||||||
|
polling.
|
||||||
|
|
||||||
|
kaizen-agentic does **not** publish this event today; the `prepare` command
|
||||||
|
writes to stdout. This spec fixes the contract so an emitter (a runner wrapper
|
||||||
|
or a thin `--emit` flag in a later iteration) and consumers agree on the shape.
|
||||||
|
|
||||||
|
## Subject
|
||||||
|
|
||||||
|
```
|
||||||
|
kaizen.schedule.prepared
|
||||||
|
```
|
||||||
|
|
||||||
|
NATS/event-bus subject, sibling to the existing `kaizen.metrics.recorded` and
|
||||||
|
`kaizen.agent.installed` subjects (Pattern 2).
|
||||||
|
|
||||||
|
## Payload
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "kaizen.schedule.prepared",
|
||||||
|
"version": "1",
|
||||||
|
"occurred_at": "2026-06-17T09:00:12Z",
|
||||||
|
"repo": "kaizen-agentic",
|
||||||
|
"root": "/home/worsch/kaizen-agentic",
|
||||||
|
"agent": "coach",
|
||||||
|
"cadence": "weekly",
|
||||||
|
"prepare_command": "kaizen-agentic schedule prepare coach --target /home/worsch/kaizen-agentic",
|
||||||
|
"bundle": {
|
||||||
|
"format": "markdown",
|
||||||
|
"agent_prompt_found": true,
|
||||||
|
"has_memory": true,
|
||||||
|
"has_metrics": true,
|
||||||
|
"pointers": ["scope", "workplans"],
|
||||||
|
"bytes": 8421
|
||||||
|
},
|
||||||
|
"session_close": [
|
||||||
|
"kaizen-agentic metrics record coach --success --time <s> --quality <0-1>"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
|
||||||
|
| Field | Type | Notes |
|
||||||
|
|-------|------|-------|
|
||||||
|
| `event` | string | Always `kaizen.schedule.prepared` |
|
||||||
|
| `version` | string | Payload schema version |
|
||||||
|
| `occurred_at` | RFC3339 | When the bundle was assembled |
|
||||||
|
| `repo` | string | State Hub slug |
|
||||||
|
| `root` | string | Absolute checkout path |
|
||||||
|
| `agent` | string | Agent the bundle orients |
|
||||||
|
| `cadence` | string | `daily` \| `weekly` \| `monthly` |
|
||||||
|
| `prepare_command` | string | Exact CLI that produced the bundle |
|
||||||
|
| `bundle.format` | string | `markdown` \| `json` |
|
||||||
|
| `bundle.agent_prompt_found` | bool | Mirrors `schedule prepare --format json` |
|
||||||
|
| `bundle.has_memory` | bool | Memory file present |
|
||||||
|
| `bundle.has_metrics` | bool | Metrics summary present |
|
||||||
|
| `bundle.pointers` | string[] | Repo pointer labels found (`scope`, `workplans`) |
|
||||||
|
| `bundle.bytes` | int | Rendered bundle size |
|
||||||
|
| `session_close` | string[] | Suggested close commands |
|
||||||
|
|
||||||
|
The `bundle.*` flags map 1:1 to the JSON output of
|
||||||
|
`kaizen-agentic schedule prepare <agent> --format json`, so an emitter can build
|
||||||
|
this payload from the existing command without new computation.
|
||||||
|
|
||||||
|
## Consumers (illustrative)
|
||||||
|
|
||||||
|
- **activity-core** — close the loop: mark the scheduled task `in_progress` when
|
||||||
|
a bundle is prepared.
|
||||||
|
- **artifact-store** — archive large bundles by reference.
|
||||||
|
- **dashboards** — fleet view of which scheduled runs have been prepared vs.
|
||||||
|
executed.
|
||||||
|
|
||||||
|
## Boundary
|
||||||
|
|
||||||
|
- The payload carries **metadata and a command**, never secrets or full repo
|
||||||
|
contents.
|
||||||
|
- kaizen-agentic owns the schema; emission wiring (NATS publish) is a future
|
||||||
|
iteration and belongs to the runner / activity-core integration.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [discover-kaizen-scheduled-repos.md](discover-kaizen-scheduled-repos.md)
|
||||||
|
- [INTEGRATION_PATTERNS.md Pattern 2](../INTEGRATION_PATTERNS.md)
|
||||||
|
- [ADR-005](../adr/ADR-005-scheduled-agent-execution.md)
|
||||||
100
docs/integrations/schedule-schema.md
Normal file
100
docs/integrations/schedule-schema.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# `.kaizen/schedule.yml` Schema
|
||||||
|
|
||||||
|
The schedule manifest declares which kaizen agents run on what cadence in an
|
||||||
|
opted-in repo. It is the repo-local half of the scheduled-agent-execution
|
||||||
|
contract (ADR-005). activity-core reads it (via the roster resolver) to fire
|
||||||
|
recurring tasks; `kaizen-agentic schedule prepare` reads it indirectly by
|
||||||
|
preparing per-agent orientation.
|
||||||
|
|
||||||
|
Canonical example: [`docs/examples/.kaizen/schedule.yml`](../examples/.kaizen/schedule.yml).
|
||||||
|
|
||||||
|
## Location
|
||||||
|
|
||||||
|
```
|
||||||
|
<project-root>/.kaizen/schedule.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Lives alongside `.kaizen/agents/` (memory) and `.kaizen/metrics/`. Like those,
|
||||||
|
its presence is the **opt-in signal** for fleet scheduling.
|
||||||
|
|
||||||
|
## Fields
|
||||||
|
|
||||||
|
| Key | Required | Type | Default | Notes |
|
||||||
|
|-----|----------|------|---------|-------|
|
||||||
|
| `version` | yes | string | — | Must be `"1"` |
|
||||||
|
| `timezone` | no | string | from ActivityDefinition | IANA tz, e.g. `Europe/Berlin` |
|
||||||
|
| `agents` | yes | mapping | — | `agent-name → settings` |
|
||||||
|
| `agents.<name>.cadence` | yes | enum | — | `daily` \| `weekly` \| `monthly` |
|
||||||
|
| `agents.<name>.cron` | no | string | cadence default | 5-field cron expression |
|
||||||
|
| `agents.<name>.enabled` | no | bool | `true` | Set `false` to declare but pause |
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "1"
|
||||||
|
timezone: Europe/Berlin
|
||||||
|
agents:
|
||||||
|
coach:
|
||||||
|
cadence: weekly
|
||||||
|
cron: "0 9 * * 1"
|
||||||
|
enabled: true
|
||||||
|
optimization:
|
||||||
|
cadence: weekly
|
||||||
|
cron: "0 10 * * 1"
|
||||||
|
enabled: true
|
||||||
|
tdd-workflow:
|
||||||
|
cadence: monthly
|
||||||
|
enabled: false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule validate
|
||||||
|
```
|
||||||
|
|
||||||
|
Errors are emitted with actionable messages and a non-zero exit code:
|
||||||
|
|
||||||
|
- Missing or non-`"1"` `version`.
|
||||||
|
- `agents` not a mapping, or no agents declared.
|
||||||
|
- An agent name that is **not** installed or packaged (typo guard).
|
||||||
|
- A `cadence` outside `daily` / `weekly` / `monthly`.
|
||||||
|
- Duplicate agent entries.
|
||||||
|
|
||||||
|
Only agents available to the project (installed under `agents/` or packaged in
|
||||||
|
the distribution) may appear in a schedule.
|
||||||
|
|
||||||
|
## Scaffolding
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule init # defaults: coach + optimization weekly
|
||||||
|
kaizen-agentic schedule init --timezone UTC # override timezone
|
||||||
|
kaizen-agentic schedule init --force # overwrite existing
|
||||||
|
kaizen-agentic schedule init --engagement coulomb-loop \
|
||||||
|
--agents coach,optimization --bootstrap-cadence hourly
|
||||||
|
```
|
||||||
|
|
||||||
|
The default scaffold enables `coach` and `optimization` weekly and declares
|
||||||
|
`tdd-workflow` monthly but **disabled** (operator opts in deliberately).
|
||||||
|
|
||||||
|
**Engagement bootstrap** (`--engagement`) writes customer-loop presets: hourly
|
||||||
|
bootstrap uses `cadence: daily` with hourly `cron` overrides (coach `:15`,
|
||||||
|
optimization `:30`). See
|
||||||
|
[customer-engagement-playbook.md](customer-engagement-playbook.md).
|
||||||
|
|
||||||
|
## Listing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule list # enabled entries only
|
||||||
|
kaizen-agentic schedule list --all # include disabled
|
||||||
|
```
|
||||||
|
|
||||||
|
## Relationship to activity-core
|
||||||
|
|
||||||
|
The `cron` field is an **optional per-repo override**. When omitted, the cadence
|
||||||
|
maps to the default cron declared in the matching ActivityDefinition (e.g.
|
||||||
|
`weekly-coach-orientation` fires Mon 09:00). This keeps fleet-wide timing in one
|
||||||
|
place while letting individual repos shift their slot.
|
||||||
|
|
||||||
|
See [ADR-005](../adr/ADR-005-scheduled-agent-execution.md) and
|
||||||
|
[INTEGRATION_PATTERNS.md Pattern 2](../INTEGRATION_PATTERNS.md).
|
||||||
64
docs/integrations/state-hub-roster-fields.md
Normal file
64
docs/integrations/state-hub-roster-fields.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# State Hub Roster Fields for Kaizen Scheduling (Design)
|
||||||
|
|
||||||
|
**Status:** design only — implemented in `the-custodian/state-hub`, not here
|
||||||
|
(repo boundary). This document specifies what kaizen-agentic and activity-core
|
||||||
|
need from the hub so the State Hub team can add the fields and filter.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
activity-core's resolver needs to answer: *which registered repos participate in
|
||||||
|
kaizen fleet scheduling, and where do they live on disk?* Today state-hub knows
|
||||||
|
the canonical repo list and `host_paths` but has no notion of schedule opt-in.
|
||||||
|
|
||||||
|
## Existing hub data (sufficient for v1)
|
||||||
|
|
||||||
|
`GET /repos/` already returns, per repo:
|
||||||
|
|
||||||
|
| Field | Use |
|
||||||
|
|-------|-----|
|
||||||
|
| `slug` | Canonical repo identifier (`target_repo` in tasks) |
|
||||||
|
| `host_paths[hostname] → local_path` | Where the repo is checked out on a runner |
|
||||||
|
| `domain` | Scope filter (e.g. `custodian`) |
|
||||||
|
|
||||||
|
For **v1**, opt-in is detected **in the repo** (`.kaizen/schedule.yml` exists and
|
||||||
|
validates). The resolver clones/reads each candidate path and checks for the
|
||||||
|
file. No hub schema change is strictly required to ship the pilot.
|
||||||
|
|
||||||
|
## Proposed hub fields (v2, optional)
|
||||||
|
|
||||||
|
To let operators query eligibility **without touching every checkout**, add an
|
||||||
|
optional repo-metadata flag:
|
||||||
|
|
||||||
|
| Field | Type | Default | Meaning |
|
||||||
|
|-------|------|---------|---------|
|
||||||
|
| `kaizen_schedule_enabled` | bool | `false` | Operator-confirmed fleet participation |
|
||||||
|
| `kaizen_schedule_updated_at` | timestamp | null | Last time schedule.yml was synced/seen |
|
||||||
|
|
||||||
|
### Suggested filter
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /repos/?kaizen_schedule_enabled=true&domain=custodian
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns only schedule-eligible repos with their `host_paths`, so the resolver
|
||||||
|
skips repos that have not opted in — cheaper than scanning every checkout.
|
||||||
|
|
||||||
|
### Write path
|
||||||
|
|
||||||
|
The flag is set by an operator (or a future `kaizen-agentic schedule register`
|
||||||
|
that calls the hub). It is **advisory**: the authoritative opt-in remains the
|
||||||
|
presence of a valid `.kaizen/schedule.yml` in the repo, re-checked by the
|
||||||
|
resolver at run time. The flag is an index, not a source of truth.
|
||||||
|
|
||||||
|
## Boundary
|
||||||
|
|
||||||
|
- kaizen-agentic does **not** write these fields in WP-0006.
|
||||||
|
- state-hub schema migration is tracked in `the-custodian`.
|
||||||
|
- The resolver (activity-core) treats the hub flag as a pre-filter and the repo
|
||||||
|
file as the decision.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [discover-kaizen-scheduled-repos.md](discover-kaizen-scheduled-repos.md)
|
||||||
|
- [schedule-schema.md](schedule-schema.md)
|
||||||
|
- [ADR-005](../adr/ADR-005-scheduled-agent-execution.md)
|
||||||
65
event-types/kaizen.metrics.recorded.md
Normal file
65
event-types/kaizen.metrics.recorded.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
type_id: kaizen.metrics.recorded
|
||||||
|
version: "1.0"
|
||||||
|
publisher: kaizen-agentic
|
||||||
|
governance: publisher-declared
|
||||||
|
status: active
|
||||||
|
---
|
||||||
|
|
||||||
|
# kaizen.metrics.recorded
|
||||||
|
|
||||||
|
## Intent
|
||||||
|
|
||||||
|
Emitted when a project records agent execution metrics via
|
||||||
|
`kaizen-agentic metrics record --emit-event`. Signals that rolling performance
|
||||||
|
summary changed and may trigger quality-escalation ActivityDefinitions.
|
||||||
|
|
||||||
|
**Owning repo:** `kaizen-agentic` — schema changes require supplier review.
|
||||||
|
|
||||||
|
## When Published
|
||||||
|
|
||||||
|
- CLI: `kaizen-agentic metrics record <agent> --emit-event` after successful append
|
||||||
|
- Not emitted when append is skipped (duplicate idempotency key)
|
||||||
|
- NATS subject: `activity.kaizen.metrics.recorded`
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
| Name | Type | Required | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| agent | string | yes | Agent name from `metrics record <agent>` |
|
||||||
|
| project | string | yes | Repo slug (`KAIZEN_PROJECT_SLUG` or directory basename) |
|
||||||
|
| summary.success_rate | float | yes | Rolling success rate from `summary.json` after append |
|
||||||
|
| summary.execution_count | integer | yes | Total execution count |
|
||||||
|
| summary.avg_quality | float | no | Average quality score (maps from `avg_quality_score`) |
|
||||||
|
|
||||||
|
## Example Payload
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"type": "kaizen.metrics.recorded",
|
||||||
|
"version": "1.0",
|
||||||
|
"timestamp": "2026-06-18T12:00:00Z",
|
||||||
|
"publisher": "kaizen-agentic",
|
||||||
|
"attributes": {
|
||||||
|
"agent": "coach",
|
||||||
|
"project": "kaizen-agentic",
|
||||||
|
"summary": {
|
||||||
|
"success_rate": 0.75,
|
||||||
|
"execution_count": 12,
|
||||||
|
"avg_quality": 0.81
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Consumer Notes
|
||||||
|
|
||||||
|
- **activity-core**: `event-payload` resolver binds `attributes` → `context.metrics`
|
||||||
|
for rules such as `flag-low-success-rate`
|
||||||
|
- **coulomb-loop**: `low-success-rate-review` (LOOP-WP-0002) — primary reactive path
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- `docs/integrations/kaizen-metrics-recorded-event.md`
|
||||||
|
- `coulomb-loop/loops/quality-escalation/event-payload.md`
|
||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "kaizen-agentic"
|
name = "kaizen-agentic"
|
||||||
version = "1.2.0"
|
version = "1.4.0"
|
||||||
description = "AI agent development framework embracing continuous improvement (kaizen)"
|
description = "AI agent development framework embracing continuous improvement (kaizen)"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = {file = "LICENSE"}
|
license = {file = "LICENSE"}
|
||||||
@@ -32,6 +32,9 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
events = [
|
||||||
|
"nats-py>=2.6.0",
|
||||||
|
]
|
||||||
dev = [
|
dev = [
|
||||||
"pytest>=6.0.0",
|
"pytest>=6.0.0",
|
||||||
"pytest-cov>=4.0.0",
|
"pytest-cov>=4.0.0",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ It also includes a comprehensive agent distribution system for sharing
|
|||||||
specialized agents across projects via CLI tools and package management.
|
specialized agents across projects via CLI tools and package management.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = "1.2.0"
|
__version__ = "1.4.0"
|
||||||
__author__ = "Kaizen Agentic Team"
|
__author__ = "Kaizen Agentic Team"
|
||||||
|
|
||||||
from .core import Agent, AgentConfig
|
from .core import Agent, AgentConfig
|
||||||
|
|||||||
73
src/kaizen_agentic/agent_docs.py
Normal file
73
src/kaizen_agentic/agent_docs.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
"""Render and idempotently upsert the project ``## Installed Agents`` section.
|
||||||
|
|
||||||
|
Single source of truth for the agent documentation block written into a
|
||||||
|
project's ``CLAUDE.md``. Both :class:`~kaizen_agentic.installer.AgentInstaller`
|
||||||
|
and the ``kaizen-agentic docs generate`` command reuse these helpers so the
|
||||||
|
section is produced and replaced the same way everywhere — and, critically,
|
||||||
|
**idempotently**: regenerating N times yields the same output as once.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Dict, Iterable, List
|
||||||
|
|
||||||
|
from .registry import AgentDefinition
|
||||||
|
|
||||||
|
SECTION_HEADING = "## Installed Agents"
|
||||||
|
SECTION_FOOTER = (
|
||||||
|
"Use these agents by referencing them in your Claude Code interactions."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Match the Installed Agents block up to the next *top-level* heading (``\n## ``,
|
||||||
|
# with a trailing space so ``### Subsection`` headings inside the block do not
|
||||||
|
# terminate the match) or end-of-file. The previous ``(?=##|\Z)`` form stopped
|
||||||
|
# at the first ``### Category`` subheading and so duplicated the block on every
|
||||||
|
# run (WP-0007 T01).
|
||||||
|
_SECTION_RE = re.compile(r"## Installed Agents.*?(?=\n## (?!#)|\Z)", re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
|
def render_installed_agents_section(agents: Iterable[AgentDefinition]) -> str:
|
||||||
|
"""Render the ``## Installed Agents`` markdown block from agent metadata.
|
||||||
|
|
||||||
|
Agents are grouped by category in first-seen order. The returned string is
|
||||||
|
newline-terminated and contains no trailing top-level heading, so it can be
|
||||||
|
spliced in front of any following section.
|
||||||
|
"""
|
||||||
|
lines: List[str] = [
|
||||||
|
SECTION_HEADING,
|
||||||
|
"",
|
||||||
|
"This project includes the following specialized agents:",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
|
||||||
|
categories: Dict[str, List[AgentDefinition]] = {}
|
||||||
|
for agent in agents:
|
||||||
|
categories.setdefault(agent.category.value, []).append(agent)
|
||||||
|
|
||||||
|
for category, members in categories.items():
|
||||||
|
lines.append(f"### {category.replace('-', ' ').title()}")
|
||||||
|
lines.append("")
|
||||||
|
for agent in members:
|
||||||
|
lines.append(f"- **{agent.name}**: {agent.description}")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.append(SECTION_FOOTER)
|
||||||
|
lines.append("")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def upsert_installed_agents_section(content: str, section: str) -> str:
|
||||||
|
"""Return ``content`` with its Installed Agents block replaced or appended.
|
||||||
|
|
||||||
|
Idempotent: if ``content`` already contains the block, exactly that block is
|
||||||
|
replaced (never duplicated); otherwise the section is appended. ``section``
|
||||||
|
is normalised to end with a single trailing blank line.
|
||||||
|
"""
|
||||||
|
section = section.rstrip() + "\n"
|
||||||
|
|
||||||
|
if SECTION_HEADING in content:
|
||||||
|
return _SECTION_RE.sub(lambda _m: section, content, count=1)
|
||||||
|
|
||||||
|
separator = "" if content.endswith("\n\n") or not content else "\n"
|
||||||
|
return content + separator + section
|
||||||
@@ -15,9 +15,27 @@ from .integrations.artifact_store import (
|
|||||||
default_api_url,
|
default_api_url,
|
||||||
publish_optimizer_evidence,
|
publish_optimizer_evidence,
|
||||||
)
|
)
|
||||||
|
from .integrations.event_bus import (
|
||||||
|
build_metrics_recorded_envelope,
|
||||||
|
publish_metrics_recorded_event,
|
||||||
|
resolve_project_slug,
|
||||||
|
)
|
||||||
from .integrations.helix import HelixCorrelationAdapter, enrich_helix_correlation
|
from .integrations.helix import HelixCorrelationAdapter, enrich_helix_correlation
|
||||||
from .metrics import MetricsStore, OptimizerStore, performance_summary_markdown
|
from .metrics import MetricsStore, OptimizerStore, performance_summary_markdown
|
||||||
from .optimization import OptimizationLoop, MIN_SAMPLES_FOR_RECOMMENDATIONS
|
from .optimization import OptimizationLoop, MIN_SAMPLES_FOR_RECOMMENDATIONS
|
||||||
|
from .engagement_promote import promote_engagement
|
||||||
|
from .schedule import (
|
||||||
|
ScheduleError,
|
||||||
|
default_schedule_yaml,
|
||||||
|
engagement_schedule_yaml,
|
||||||
|
load_schedule,
|
||||||
|
schedule_path,
|
||||||
|
validate_schedule,
|
||||||
|
)
|
||||||
|
from .agent_docs import (
|
||||||
|
render_installed_agents_section,
|
||||||
|
upsert_installed_agents_section,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def safe_cli_wrapper():
|
def safe_cli_wrapper():
|
||||||
@@ -411,8 +429,21 @@ def validate(target: str):
|
|||||||
|
|
||||||
target_path = Path(target).resolve()
|
target_path = Path(target).resolve()
|
||||||
|
|
||||||
|
# Validate agent frontmatter schema
|
||||||
|
click.echo("Validating agent frontmatter schema...")
|
||||||
|
schema_errors = registry.validate_frontmatter_schema()
|
||||||
|
|
||||||
|
if schema_errors:
|
||||||
|
click.echo("Frontmatter schema errors:")
|
||||||
|
for agent_file, errors in schema_errors.items():
|
||||||
|
click.echo(f" {agent_file}:")
|
||||||
|
for error in errors:
|
||||||
|
click.echo(f" ❌ {error}")
|
||||||
|
else:
|
||||||
|
click.echo(" ✅ Frontmatter schema validation passed")
|
||||||
|
|
||||||
# Validate registry agents
|
# Validate registry agents
|
||||||
click.echo("Validating agent registry...")
|
click.echo("\nValidating agent registry...")
|
||||||
registry_errors = registry.validate_agents()
|
registry_errors = registry.validate_agents()
|
||||||
|
|
||||||
if registry_errors:
|
if registry_errors:
|
||||||
@@ -1045,6 +1076,11 @@ def metrics():
|
|||||||
@click.option(
|
@click.option(
|
||||||
"--json", "json_input", is_flag=True, help="Read full record JSON from stdin"
|
"--json", "json_input", is_flag=True, help="Read full record JSON from stdin"
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--emit-event",
|
||||||
|
is_flag=True,
|
||||||
|
help="Publish kaizen.metrics.recorded to NATS (requires nats-py)",
|
||||||
|
)
|
||||||
def metrics_record(
|
def metrics_record(
|
||||||
agent_name: str,
|
agent_name: str,
|
||||||
target: str,
|
target: str,
|
||||||
@@ -1055,6 +1091,7 @@ def metrics_record(
|
|||||||
session_id: Optional[str],
|
session_id: Optional[str],
|
||||||
idempotency_key: Optional[str],
|
idempotency_key: Optional[str],
|
||||||
json_input: bool,
|
json_input: bool,
|
||||||
|
emit_event: bool,
|
||||||
):
|
):
|
||||||
"""Append one execution record for an agent."""
|
"""Append one execution record for an agent."""
|
||||||
store = MetricsStore(_project_root(target), agent_name)
|
store = MetricsStore(_project_root(target), agent_name)
|
||||||
@@ -1085,6 +1122,21 @@ def metrics_record(
|
|||||||
|
|
||||||
if store.append(payload, idempotency_key=idempotency_key):
|
if store.append(payload, idempotency_key=idempotency_key):
|
||||||
click.echo(f"Recorded metrics for '{agent_name}'")
|
click.echo(f"Recorded metrics for '{agent_name}'")
|
||||||
|
if emit_event:
|
||||||
|
summary = store.read_summary() or store.write_summary()
|
||||||
|
envelope = build_metrics_recorded_envelope(
|
||||||
|
agent=agent_name,
|
||||||
|
project=resolve_project_slug(store.project_root),
|
||||||
|
summary=summary,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
subject = publish_metrics_recorded_event(envelope)
|
||||||
|
except RuntimeError as exc:
|
||||||
|
click.echo(f"Error: {exc}", err=True)
|
||||||
|
sys.exit(1)
|
||||||
|
click.echo(
|
||||||
|
f"Emitted kaizen.metrics.recorded for '{agent_name}' → {subject}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
click.echo(
|
click.echo(
|
||||||
f"Skipped duplicate record for '{agent_name}' (idempotency key exists)"
|
f"Skipped duplicate record for '{agent_name}' (idempotency key exists)"
|
||||||
@@ -1360,6 +1412,518 @@ def protocols_show(agent_name: str, slug: str):
|
|||||||
click.echo(protocol_path.read_text())
|
click.echo(protocol_path.read_text())
|
||||||
|
|
||||||
|
|
||||||
|
@cli.group()
|
||||||
|
def schedule():
|
||||||
|
"""Prepare and validate scheduled agent runs (.kaizen/schedule.yml, ADR-005).
|
||||||
|
|
||||||
|
kaizen-agentic does not run cron schedules or invoke Claude. activity-core
|
||||||
|
fires the cron and creates a task per (repo, agent); a coding-agent session
|
||||||
|
runs `schedule prepare <agent>` to assemble orientation, then executes the
|
||||||
|
agent instructions.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@schedule.command("validate")
|
||||||
|
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||||
|
def schedule_validate(target: str):
|
||||||
|
"""Validate .kaizen/schedule.yml against the ADR-005 schema."""
|
||||||
|
path = schedule_path(_project_root(target))
|
||||||
|
|
||||||
|
try:
|
||||||
|
parsed = load_schedule(path)
|
||||||
|
except ScheduleError as exc:
|
||||||
|
click.echo(f"❌ {exc}", err=True)
|
||||||
|
click.echo(" Run: kaizen-agentic schedule init", err=True)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
known_agents = _get_registry().agent_names()
|
||||||
|
errors = validate_schedule(parsed, known_agents=known_agents)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
click.echo(f"❌ Schedule validation failed ({path}):")
|
||||||
|
for error in errors:
|
||||||
|
click.echo(f" • {error}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
enabled = parsed.enabled_entries()
|
||||||
|
click.echo(f"✅ Schedule valid: {path}")
|
||||||
|
click.echo(f" {len(parsed.entries)} agent(s), {len(enabled)} enabled")
|
||||||
|
|
||||||
|
|
||||||
|
@schedule.command("init")
|
||||||
|
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||||
|
@click.option(
|
||||||
|
"--timezone", default="Europe/Berlin", show_default=True, help="Schedule timezone"
|
||||||
|
)
|
||||||
|
@click.option("--force", is_flag=True, help="Overwrite an existing schedule.yml")
|
||||||
|
@click.option(
|
||||||
|
"--engagement",
|
||||||
|
default=None,
|
||||||
|
help="Customer engagement slug (bootstrap schedule for target repos)",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--agents",
|
||||||
|
default=None,
|
||||||
|
help="Comma-separated agents for --engagement (default: coach,optimization)",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--bootstrap-cadence",
|
||||||
|
type=click.Choice(["hourly", "daily", "weekly"]),
|
||||||
|
default="hourly",
|
||||||
|
show_default=True,
|
||||||
|
help="Cadence preset for --engagement (hourly uses daily enum + hourly cron)",
|
||||||
|
)
|
||||||
|
def schedule_init(
|
||||||
|
target: str,
|
||||||
|
timezone: str,
|
||||||
|
force: bool,
|
||||||
|
engagement: Optional[str],
|
||||||
|
agents: Optional[str],
|
||||||
|
bootstrap_cadence: str,
|
||||||
|
):
|
||||||
|
"""Scaffold .kaizen/schedule.yml (weekly default or engagement bootstrap)."""
|
||||||
|
if (agents or bootstrap_cadence != "hourly") and not engagement:
|
||||||
|
click.echo(
|
||||||
|
"Error: --agents and --bootstrap-cadence require --engagement",
|
||||||
|
err=True,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
path = schedule_path(_project_root(target))
|
||||||
|
|
||||||
|
if path.exists() and not force:
|
||||||
|
click.echo(f"Schedule already exists: {path}")
|
||||||
|
click.echo(" Use --force to overwrite.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if engagement:
|
||||||
|
agent_list = (
|
||||||
|
[item.strip() for item in agents.split(",") if item.strip()]
|
||||||
|
if agents
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
known_agents = _get_registry().agent_names()
|
||||||
|
if agent_list:
|
||||||
|
unknown = [name for name in agent_list if name not in known_agents]
|
||||||
|
if unknown:
|
||||||
|
click.echo(
|
||||||
|
f"Error: unknown agent(s) for engagement schedule: {', '.join(unknown)}",
|
||||||
|
err=True,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
try:
|
||||||
|
yaml_text = engagement_schedule_yaml(
|
||||||
|
engagement,
|
||||||
|
agents=agent_list,
|
||||||
|
bootstrap_cadence=bootstrap_cadence,
|
||||||
|
timezone=timezone,
|
||||||
|
)
|
||||||
|
except ScheduleError as exc:
|
||||||
|
click.echo(f"Error: {exc}", err=True)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
yaml_text = default_schedule_yaml(timezone=timezone)
|
||||||
|
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(yaml_text, encoding="utf-8")
|
||||||
|
click.echo(f"Initialized schedule: {path}")
|
||||||
|
if engagement:
|
||||||
|
click.echo(
|
||||||
|
f" Engagement: {engagement} (bootstrap-cadence={bootstrap_cadence})"
|
||||||
|
)
|
||||||
|
click.echo(" Validate with: kaizen-agentic schedule validate")
|
||||||
|
|
||||||
|
|
||||||
|
@schedule.command("promote")
|
||||||
|
@click.option(
|
||||||
|
"--engagement-repo",
|
||||||
|
"-e",
|
||||||
|
required=True,
|
||||||
|
type=click.Path(exists=True, file_okay=False, path_type=Path),
|
||||||
|
help="Customer engagement repo (e.g. coulomb-loop)",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--engagement",
|
||||||
|
default="coulomb-loop",
|
||||||
|
show_default=True,
|
||||||
|
help="Engagement slug written into fleet schedule.yml headers",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--to-phase",
|
||||||
|
type=click.Choice(["stabilize", "operate"]),
|
||||||
|
default=None,
|
||||||
|
help="Target phase (default: next phase after current)",
|
||||||
|
)
|
||||||
|
@click.option("--loop", default=None, help="Promote a single loop id only")
|
||||||
|
@click.option("--dry-run", is_flag=True, help="Print planned actions without writing")
|
||||||
|
@click.option("--skip-cadence", is_flag=True, help="Skip loops/*/cadence.yml updates")
|
||||||
|
@click.option(
|
||||||
|
"--skip-definitions", is_flag=True, help="Skip activity-definitions transforms"
|
||||||
|
)
|
||||||
|
@click.option("--skip-fleet", is_flag=True, help="Skip .kaizen/schedule.yml on roster")
|
||||||
|
@click.option("--skip-sync", is_flag=True, help="Skip activity-core definition sync")
|
||||||
|
@click.option(
|
||||||
|
"--fleet-only",
|
||||||
|
is_flag=True,
|
||||||
|
help="Only update fleet schedule.yml (+ sync unless --skip-sync)",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--activity-core",
|
||||||
|
type=click.Path(exists=True, file_okay=False, path_type=Path),
|
||||||
|
default=None,
|
||||||
|
help="activity-core repo root (or ACTIVITY_CORE_ROOT env)",
|
||||||
|
)
|
||||||
|
def schedule_promote(
|
||||||
|
engagement_repo: Path,
|
||||||
|
engagement: str,
|
||||||
|
to_phase: Optional[str],
|
||||||
|
loop: Optional[str],
|
||||||
|
dry_run: bool,
|
||||||
|
skip_cadence: bool,
|
||||||
|
skip_definitions: bool,
|
||||||
|
skip_fleet: bool,
|
||||||
|
skip_sync: bool,
|
||||||
|
fleet_only: bool,
|
||||||
|
activity_core: Optional[Path],
|
||||||
|
):
|
||||||
|
"""Atomically promote cadence across cadence.yml, definitions, fleet, and sync."""
|
||||||
|
if fleet_only:
|
||||||
|
skip_cadence = True
|
||||||
|
skip_definitions = True
|
||||||
|
if to_phase is None:
|
||||||
|
to_phase = "operate"
|
||||||
|
|
||||||
|
result = promote_engagement(
|
||||||
|
engagement_repo,
|
||||||
|
engagement_slug=engagement,
|
||||||
|
to_phase=to_phase,
|
||||||
|
loop=loop,
|
||||||
|
dry_run=dry_run,
|
||||||
|
skip_cadence=skip_cadence,
|
||||||
|
skip_definitions=skip_definitions,
|
||||||
|
skip_fleet=skip_fleet,
|
||||||
|
skip_sync=skip_sync,
|
||||||
|
activity_core_root=activity_core,
|
||||||
|
)
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
click.echo("Dry run — planned actions:")
|
||||||
|
else:
|
||||||
|
click.echo("Promotion complete — actions:")
|
||||||
|
|
||||||
|
by_layer: dict[str, list[str]] = {}
|
||||||
|
for action in result.actions:
|
||||||
|
by_layer.setdefault(action.layer, []).append(action.description)
|
||||||
|
|
||||||
|
for layer in ("cadence", "definitions", "fleet", "sync"):
|
||||||
|
items = by_layer.get(layer)
|
||||||
|
if items:
|
||||||
|
click.echo(f"\n[{layer}]")
|
||||||
|
for item in items:
|
||||||
|
click.echo(f" • {item}")
|
||||||
|
|
||||||
|
if result.errors:
|
||||||
|
click.echo("\nWarnings / errors:", err=True)
|
||||||
|
for err in result.errors:
|
||||||
|
click.echo(f" ! {err}", err=True)
|
||||||
|
if not result.actions:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not result.actions and not result.errors:
|
||||||
|
click.echo("Nothing to do — layers already aligned.")
|
||||||
|
|
||||||
|
|
||||||
|
@schedule.command("list")
|
||||||
|
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||||
|
@click.option("--all", "show_all", is_flag=True, help="Include disabled entries")
|
||||||
|
def schedule_list(target: str, show_all: bool):
|
||||||
|
"""Show enabled schedule entries from .kaizen/schedule.yml."""
|
||||||
|
path = schedule_path(_project_root(target))
|
||||||
|
|
||||||
|
try:
|
||||||
|
parsed = load_schedule(path)
|
||||||
|
except ScheduleError as exc:
|
||||||
|
click.echo(f"No schedule found: {exc}")
|
||||||
|
click.echo(" Run: kaizen-agentic schedule init")
|
||||||
|
return
|
||||||
|
|
||||||
|
entries = parsed.entries if show_all else parsed.enabled_entries()
|
||||||
|
if not entries:
|
||||||
|
click.echo("No enabled schedule entries (use --all to see disabled).")
|
||||||
|
return
|
||||||
|
|
||||||
|
click.echo(f"Scheduled agents ({path}):")
|
||||||
|
if parsed.timezone:
|
||||||
|
click.echo(f" Timezone: {parsed.timezone}")
|
||||||
|
for entry in entries:
|
||||||
|
flag = "✅" if entry.enabled else "⏸ "
|
||||||
|
cron = f" cron={entry.cron}" if entry.cron else ""
|
||||||
|
click.echo(f" {flag} {entry.agent}: {entry.cadence}{cron}")
|
||||||
|
|
||||||
|
|
||||||
|
@schedule.command("prepare")
|
||||||
|
@click.argument("agent_name")
|
||||||
|
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||||
|
@click.option(
|
||||||
|
"--format",
|
||||||
|
"output_format",
|
||||||
|
type=click.Choice(["markdown", "json"]),
|
||||||
|
default="markdown",
|
||||||
|
show_default=True,
|
||||||
|
help="Output format for the orientation bundle",
|
||||||
|
)
|
||||||
|
def schedule_prepare(agent_name: str, target: str, output_format: str):
|
||||||
|
"""Assemble an orientation bundle for a scheduled agent run.
|
||||||
|
|
||||||
|
Bundles the agent prompt, project memory, metrics summary, and repo
|
||||||
|
pointers into a single payload. Works offline from local `.kaizen/` state;
|
||||||
|
no State Hub required. Pass the output to a coding-agent session.
|
||||||
|
"""
|
||||||
|
bundle = _build_prepare_bundle(agent_name, _project_root(target))
|
||||||
|
|
||||||
|
if output_format == "json":
|
||||||
|
click.echo(json.dumps(bundle, indent=2))
|
||||||
|
return
|
||||||
|
|
||||||
|
click.echo(_render_prepare_markdown(bundle))
|
||||||
|
|
||||||
|
|
||||||
|
def _build_prepare_bundle(agent_name: str, project_root: Path) -> dict:
|
||||||
|
"""Collect the orientation bundle pieces for `schedule prepare`."""
|
||||||
|
registry = _get_registry()
|
||||||
|
agent_path = registry.get_agent_path(agent_name)
|
||||||
|
agent_prompt = agent_path.read_text(encoding="utf-8") if agent_path else None
|
||||||
|
|
||||||
|
memory_path = project_root / ".kaizen" / "agents" / agent_name / "memory.md"
|
||||||
|
memory = memory_path.read_text(encoding="utf-8") if memory_path.exists() else None
|
||||||
|
|
||||||
|
metrics_store = MetricsStore(project_root, agent_name)
|
||||||
|
metrics_summary = metrics_store.read_summary()
|
||||||
|
if metrics_summary is None and metrics_store.executions_path.exists():
|
||||||
|
metrics_summary = metrics_store.write_summary()
|
||||||
|
|
||||||
|
pointers = {}
|
||||||
|
scope_path = project_root / "SCOPE.md"
|
||||||
|
if scope_path.exists():
|
||||||
|
pointers["scope"] = str(scope_path)
|
||||||
|
workplans_path = project_root / "workplans"
|
||||||
|
if workplans_path.is_dir():
|
||||||
|
pointers["workplans"] = str(workplans_path)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"agent": agent_name,
|
||||||
|
"project": project_root.name,
|
||||||
|
"generated": _today(),
|
||||||
|
"agent_prompt": agent_prompt,
|
||||||
|
"agent_prompt_found": agent_prompt is not None,
|
||||||
|
"memory": memory,
|
||||||
|
"metrics_summary": metrics_summary,
|
||||||
|
"pointers": pointers,
|
||||||
|
"session_close": [
|
||||||
|
f"kaizen-agentic metrics record {agent_name} --success "
|
||||||
|
f"--time <seconds> --quality <0-1>",
|
||||||
|
f"Update memory: kaizen-agentic memory show {agent_name}",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _render_prepare_markdown(bundle: dict) -> str:
|
||||||
|
agent = bundle["agent"]
|
||||||
|
lines = [
|
||||||
|
f"# Scheduled Run Orientation: {agent}",
|
||||||
|
f"Project: {bundle['project']}",
|
||||||
|
f"Generated: {bundle['generated']}",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
|
||||||
|
summary = bundle.get("metrics_summary")
|
||||||
|
block = performance_summary_markdown(summary or {})
|
||||||
|
if block:
|
||||||
|
lines.append(block)
|
||||||
|
|
||||||
|
lines.append("## Agent Prompt")
|
||||||
|
if bundle["agent_prompt_found"]:
|
||||||
|
lines.append(bundle["agent_prompt"])
|
||||||
|
else:
|
||||||
|
lines.append(
|
||||||
|
f"(agent '{agent}' not found in registry — " f"run: kaizen-agentic list)"
|
||||||
|
)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.append("## Project Memory")
|
||||||
|
if bundle.get("memory"):
|
||||||
|
lines.append(bundle["memory"])
|
||||||
|
else:
|
||||||
|
lines.append(f"(none — run: kaizen-agentic memory init {agent})")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
pointers = bundle.get("pointers") or {}
|
||||||
|
lines.append("## Repo Pointers")
|
||||||
|
if pointers:
|
||||||
|
for label, path in pointers.items():
|
||||||
|
lines.append(f"- {label}: {path}")
|
||||||
|
else:
|
||||||
|
lines.append("- (no SCOPE.md / workplans/ found)")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.append("## Session Close")
|
||||||
|
for cmd in bundle["session_close"]:
|
||||||
|
lines.append(f"- `{cmd}`")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("create-agent")
|
||||||
|
@click.argument("name")
|
||||||
|
@click.option(
|
||||||
|
"--category",
|
||||||
|
"-c",
|
||||||
|
type=click.Choice([c.value for c in AgentCategory]),
|
||||||
|
help="Agent category (prompted if omitted)",
|
||||||
|
)
|
||||||
|
@click.option("--description", "-d", help="One-line description (prompted if omitted)")
|
||||||
|
@click.option(
|
||||||
|
"--memory",
|
||||||
|
type=click.Choice(["enabled", "disabled"]),
|
||||||
|
default="enabled",
|
||||||
|
show_default=True,
|
||||||
|
help="Project memory support",
|
||||||
|
)
|
||||||
|
@click.option("--model", help="Optional model hint (e.g. claude-opus-4-8)")
|
||||||
|
@click.option(
|
||||||
|
"--target",
|
||||||
|
"-t",
|
||||||
|
default=".",
|
||||||
|
help="Project root containing agents/ (default: current)",
|
||||||
|
)
|
||||||
|
@click.option("--force", is_flag=True, help="Overwrite an existing agent file")
|
||||||
|
def create_agent(
|
||||||
|
name: str,
|
||||||
|
category: Optional[str],
|
||||||
|
description: Optional[str],
|
||||||
|
memory: str,
|
||||||
|
model: Optional[str],
|
||||||
|
target: str,
|
||||||
|
force: bool,
|
||||||
|
):
|
||||||
|
"""Scaffold a new schema-valid agent definition (agents/agent-<name>.md)."""
|
||||||
|
if not category:
|
||||||
|
category = click.prompt(
|
||||||
|
"Category", type=click.Choice([c.value for c in AgentCategory])
|
||||||
|
)
|
||||||
|
if not description:
|
||||||
|
description = click.prompt("One-line description")
|
||||||
|
|
||||||
|
agents_dir = _project_root(target) / "agents"
|
||||||
|
agents_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
agent_path = agents_dir / f"agent-{name}.md"
|
||||||
|
|
||||||
|
if agent_path.exists() and not force:
|
||||||
|
click.echo(f"Agent already exists: {agent_path}")
|
||||||
|
click.echo(" Use --force to overwrite.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
frontmatter = [
|
||||||
|
"---",
|
||||||
|
f"name: {name}",
|
||||||
|
f"description: {description}",
|
||||||
|
f"category: {category}",
|
||||||
|
f"memory: {memory}",
|
||||||
|
]
|
||||||
|
if model:
|
||||||
|
frontmatter.append(f"model: {model}")
|
||||||
|
frontmatter.append("---")
|
||||||
|
|
||||||
|
title = name.replace("-", " ").title()
|
||||||
|
body = f"""
|
||||||
|
# {title} Agent
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
<!-- One paragraph: what this agent does and what it does not do. -->
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
<!-- Triggers and situations where this agent should be invoked. -->
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
<!-- Step-by-step guidance the agent follows. -->
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
<!-- What the agent produces and in what format. -->
|
||||||
|
"""
|
||||||
|
agent_path.write_text("\n".join(frontmatter) + "\n" + body)
|
||||||
|
|
||||||
|
# Validate the scaffold passes the frontmatter schema (T03).
|
||||||
|
errors = (
|
||||||
|
AgentRegistry(agents_dir).validate_frontmatter_schema().get(agent_path.name, [])
|
||||||
|
)
|
||||||
|
if errors:
|
||||||
|
click.echo(f"⚠️ Created {agent_path} but it has schema issues:")
|
||||||
|
for error in errors:
|
||||||
|
click.echo(f" ❌ {error}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
click.echo(f"✅ Created agent: {agent_path}")
|
||||||
|
click.echo(" Edit the skeleton, then validate: kaizen-agentic validate")
|
||||||
|
click.echo(" Before release: make agents-sync-package")
|
||||||
|
|
||||||
|
|
||||||
|
@cli.group()
|
||||||
|
def docs():
|
||||||
|
"""Generate project documentation from agent metadata."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@docs.command("generate")
|
||||||
|
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||||
|
@click.option(
|
||||||
|
"--check",
|
||||||
|
is_flag=True,
|
||||||
|
help="Exit non-zero if CLAUDE.md would change (do not write)",
|
||||||
|
)
|
||||||
|
def docs_generate(target: str, check: bool):
|
||||||
|
"""Refresh the '## Installed Agents' section of CLAUDE.md (idempotent)."""
|
||||||
|
target_path = _project_root(target)
|
||||||
|
# Resolve agents from the target project's own agents/ when present, so
|
||||||
|
# `docs generate --target other/project` documents that project's agents
|
||||||
|
# rather than the registry resolved from the current directory.
|
||||||
|
local_agents = target_path / "agents"
|
||||||
|
registry = AgentRegistry(local_agents) if local_agents.exists() else _get_registry()
|
||||||
|
installer = AgentInstaller(registry)
|
||||||
|
|
||||||
|
installed = installer.list_installed_agents(target_path)
|
||||||
|
if not installed:
|
||||||
|
click.echo("No agents installed in this project — nothing to document.")
|
||||||
|
click.echo(" Run: kaizen-agentic install <agents>")
|
||||||
|
return
|
||||||
|
|
||||||
|
agents = [a for a in (registry.get_agent(n) for n in installed) if a is not None]
|
||||||
|
section = render_installed_agents_section(agents)
|
||||||
|
|
||||||
|
claude_md = target_path / "CLAUDE.md"
|
||||||
|
current = claude_md.read_text() if claude_md.exists() else ""
|
||||||
|
updated = upsert_installed_agents_section(current, section)
|
||||||
|
|
||||||
|
if check:
|
||||||
|
if updated != current:
|
||||||
|
click.echo(f"❌ CLAUDE.md is out of date: {claude_md}")
|
||||||
|
click.echo(" Run: kaizen-agentic docs generate")
|
||||||
|
sys.exit(1)
|
||||||
|
click.echo(f"✅ CLAUDE.md is up to date ({len(agents)} agents)")
|
||||||
|
return
|
||||||
|
|
||||||
|
if updated == current:
|
||||||
|
click.echo(f"CLAUDE.md already up to date ({len(agents)} agents)")
|
||||||
|
return
|
||||||
|
|
||||||
|
claude_md.write_text(updated)
|
||||||
|
click.echo(f"Updated Installed Agents section ({len(agents)} agents): {claude_md}")
|
||||||
|
|
||||||
|
|
||||||
def _project_root(target: str) -> Path:
|
def _project_root(target: str) -> Path:
|
||||||
return Path(target).resolve()
|
return Path(target).resolve()
|
||||||
|
|
||||||
|
|||||||
555
src/kaizen_agentic/engagement_promote.py
Normal file
555
src/kaizen_agentic/engagement_promote.py
Normal file
@@ -0,0 +1,555 @@
|
|||||||
|
"""Atomic cadence promotion across engagement contract layers (ADR-003).
|
||||||
|
|
||||||
|
Orchestrates three layers that must stay aligned on promotion:
|
||||||
|
|
||||||
|
1. Customer policy — ``loops/*/cadence.yml`` in the engagement repo
|
||||||
|
2. Scheduler contract — ``activity-definitions/*.md`` → activity-core sync
|
||||||
|
3. Fleet opt-in — ``.kaizen/schedule.yml`` on roster target repos
|
||||||
|
|
||||||
|
See coulomb-loop ADR-002 / ADR-003 and supplier customer-engagement-playbook.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import date
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from .schedule import ScheduleError, engagement_schedule_yaml, schedule_path
|
||||||
|
|
||||||
|
PHASE_ORDER = ("bootstrap", "stabilize", "operate")
|
||||||
|
PHASE_FILE_PREFIX = {
|
||||||
|
"bootstrap": "hourly",
|
||||||
|
"stabilize": "daily",
|
||||||
|
"operate": "weekly",
|
||||||
|
}
|
||||||
|
CADENCE_ENUM = {
|
||||||
|
"bootstrap": "daily", # hourly crons keep daily enum for resolver filter
|
||||||
|
"stabilize": "daily",
|
||||||
|
"operate": "weekly",
|
||||||
|
}
|
||||||
|
|
||||||
|
LOOP_DIR_BY_ID = {
|
||||||
|
"kaizen-improvement-stack": "kaizen-stack",
|
||||||
|
"quality-escalation": "quality-escalation",
|
||||||
|
"registry-hygiene": "registry-hygiene",
|
||||||
|
"loop-regulator": "regulator",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Activity-definition stems per loop (without phase prefix). Crons come from
|
||||||
|
# operate_target in cadence.yml when present, else these defaults.
|
||||||
|
DEFAULT_OPERATE_CRONS: dict[str, dict[str, str]] = {
|
||||||
|
"kaizen-improvement-stack": {
|
||||||
|
"metrics-optimize": "0 8 * * 1",
|
||||||
|
"coach-orientation": "0 9 * * 1",
|
||||||
|
"optimization-review": "0 10 * * 1",
|
||||||
|
},
|
||||||
|
"registry-hygiene": {
|
||||||
|
"registry-hygiene-sweep": "0 9 * * 1",
|
||||||
|
},
|
||||||
|
"quality-escalation": {
|
||||||
|
"metrics-health-sweep": "0 6 * * 1",
|
||||||
|
},
|
||||||
|
"loop-regulator": {
|
||||||
|
"loop-health-collector": "0 11 * * 1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
EVENT_DEFINITIONS = frozenset({"low-success-rate-review"})
|
||||||
|
|
||||||
|
|
||||||
|
class PromoteError(Exception):
|
||||||
|
"""Raised when promotion cannot proceed."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PromoteAction:
|
||||||
|
layer: str
|
||||||
|
description: str
|
||||||
|
path: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PromoteResult:
|
||||||
|
actions: list[PromoteAction] = field(default_factory=list)
|
||||||
|
errors: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ok(self) -> bool:
|
||||||
|
return not self.errors
|
||||||
|
|
||||||
|
|
||||||
|
def _next_phase(current: str) -> str | None:
|
||||||
|
try:
|
||||||
|
idx = PHASE_ORDER.index(current)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
if idx + 1 >= len(PHASE_ORDER):
|
||||||
|
return None
|
||||||
|
return PHASE_ORDER[idx + 1]
|
||||||
|
|
||||||
|
|
||||||
|
def _loop_paths(engagement_repo: Path) -> dict[str, Path]:
|
||||||
|
loops_root = engagement_repo / "loops"
|
||||||
|
if not loops_root.is_dir():
|
||||||
|
raise PromoteError(f"loops/ not found under {engagement_repo}")
|
||||||
|
mapping: dict[str, Path] = {}
|
||||||
|
for loop_id, dirname in LOOP_DIR_BY_ID.items():
|
||||||
|
path = loops_root / dirname / "cadence.yml"
|
||||||
|
if path.is_file():
|
||||||
|
mapping[loop_id] = path
|
||||||
|
if not mapping:
|
||||||
|
raise PromoteError(f"no loops/*/cadence.yml found under {engagement_repo}")
|
||||||
|
return mapping
|
||||||
|
|
||||||
|
|
||||||
|
def _load_yaml(path: Path) -> dict[str, Any]:
|
||||||
|
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise PromoteError(f"expected mapping in {path}")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def _write_yaml(path: Path, data: dict[str, Any]) -> None:
|
||||||
|
path.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _split_markdown_frontmatter(text: str) -> tuple[dict[str, Any], str]:
|
||||||
|
if not text.startswith("---"):
|
||||||
|
raise PromoteError("activity definition missing YAML frontmatter")
|
||||||
|
parts = text.split("---", 2)
|
||||||
|
if len(parts) < 3:
|
||||||
|
raise PromoteError("activity definition frontmatter not closed")
|
||||||
|
meta = yaml.safe_load(parts[1])
|
||||||
|
if not isinstance(meta, dict):
|
||||||
|
raise PromoteError("activity definition frontmatter must be a mapping")
|
||||||
|
body = parts[2].lstrip("\n")
|
||||||
|
return meta, body
|
||||||
|
|
||||||
|
|
||||||
|
def _render_markdown(meta: dict[str, Any], body: str) -> str:
|
||||||
|
header = yaml.safe_dump(meta, sort_keys=False)
|
||||||
|
return f"---\n{header}---\n\n{body}"
|
||||||
|
|
||||||
|
|
||||||
|
def _roster_repos(engagement_repo: Path) -> list[tuple[str, Path, list[str]]]:
|
||||||
|
roster_path = engagement_repo / "loops" / "kaizen-stack" / "roster.yaml"
|
||||||
|
if not roster_path.is_file():
|
||||||
|
return []
|
||||||
|
data = _load_yaml(roster_path)
|
||||||
|
active = data.get("active") or []
|
||||||
|
repos: list[tuple[str, Path, list[str]]] = []
|
||||||
|
for entry in active:
|
||||||
|
if not isinstance(entry, dict):
|
||||||
|
continue
|
||||||
|
slug = str(entry.get("slug", ""))
|
||||||
|
root = entry.get("root")
|
||||||
|
agents = entry.get("agents") or ["coach", "optimization"]
|
||||||
|
if slug and root:
|
||||||
|
repos.append((slug, Path(str(root)), list(agents)))
|
||||||
|
return repos
|
||||||
|
|
||||||
|
|
||||||
|
def _operate_crons(loop_id: str, cadence: dict[str, Any]) -> dict[str, str]:
|
||||||
|
target = cadence.get("operate_target")
|
||||||
|
crons = dict(DEFAULT_OPERATE_CRONS.get(loop_id, {}))
|
||||||
|
if not isinstance(target, dict):
|
||||||
|
return crons
|
||||||
|
if loop_id == "kaizen-improvement-stack":
|
||||||
|
chain = target.get("chain")
|
||||||
|
if isinstance(chain, dict):
|
||||||
|
for stem, cron_key in [
|
||||||
|
("metrics-optimize", "metrics"),
|
||||||
|
("coach-orientation", "coach"),
|
||||||
|
("optimization-review", "optimization"),
|
||||||
|
]:
|
||||||
|
if cron_key in chain:
|
||||||
|
crons[stem] = str(chain[cron_key])
|
||||||
|
elif loop_id == "registry-hygiene" and "cron" in target:
|
||||||
|
crons["registry-hygiene-sweep"] = str(target["cron"])
|
||||||
|
elif loop_id == "quality-escalation" and "sweep_cron" in target:
|
||||||
|
crons["metrics-health-sweep"] = str(target["sweep_cron"])
|
||||||
|
elif loop_id == "loop-regulator":
|
||||||
|
if "collector_cron" in target:
|
||||||
|
crons["loop-health-collector"] = str(target["collector_cron"])
|
||||||
|
return crons
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_operate_target(
|
||||||
|
cadence: dict[str, Any], loop_id: str, promoted_at: str
|
||||||
|
) -> None:
|
||||||
|
target = cadence.pop("operate_target", None)
|
||||||
|
cadence["phase"] = "operate"
|
||||||
|
cadence["promoted_at"] = promoted_at
|
||||||
|
if not isinstance(target, dict):
|
||||||
|
target = {}
|
||||||
|
|
||||||
|
if loop_id == "kaizen-improvement-stack":
|
||||||
|
chain = target.get("chain") or DEFAULT_OPERATE_CRONS[loop_id]
|
||||||
|
cadence["cron"] = chain.get("metrics", "0 8 * * 1")
|
||||||
|
cadence["chain"] = {
|
||||||
|
"metrics": chain.get("metrics", "0 8 * * 1"),
|
||||||
|
"coach": chain.get("coach", "0 9 * * 1"),
|
||||||
|
"optimization": chain.get("optimization", "0 10 * * 1"),
|
||||||
|
}
|
||||||
|
elif loop_id == "registry-hygiene":
|
||||||
|
cadence["cron"] = target.get("cron", "0 9 * * 1")
|
||||||
|
cadence["batch_size"] = target.get("batch_size", 2)
|
||||||
|
cadence["domain_rotation"] = target.get("domain_rotation", "weekly")
|
||||||
|
elif loop_id == "quality-escalation":
|
||||||
|
cadence["cron"] = target.get("sweep_cron", "0 6 * * 1")
|
||||||
|
cadence["sweep_fallback"] = "weekly-metrics-health-sweep"
|
||||||
|
cadence.pop("stabilize_target", None)
|
||||||
|
elif loop_id == "loop-regulator":
|
||||||
|
cron = target.get("collector_cron", "0 11 * * 1")
|
||||||
|
cadence["collector_cron"] = cron
|
||||||
|
cadence["regulator_session_cron"] = target.get("regulator_session_cron", cron)
|
||||||
|
|
||||||
|
|
||||||
|
def _transform_definition(
|
||||||
|
path: Path,
|
||||||
|
from_prefix: str,
|
||||||
|
to_prefix: str,
|
||||||
|
cron: str,
|
||||||
|
cadence_enum: str,
|
||||||
|
) -> Path:
|
||||||
|
meta, body = _split_markdown_frontmatter(path.read_text(encoding="utf-8"))
|
||||||
|
old_id = str(meta.get("id", ""))
|
||||||
|
stem = path.stem.removeprefix(f"{from_prefix}-")
|
||||||
|
new_id = f"coulomb-{to_prefix}-{stem.replace('-', '-')}"
|
||||||
|
if old_id.startswith("coulomb-"):
|
||||||
|
new_id = old_id.replace(f"coulomb-{from_prefix}-", f"coulomb-{to_prefix}-", 1)
|
||||||
|
|
||||||
|
meta["id"] = new_id
|
||||||
|
if isinstance(meta.get("name"), str):
|
||||||
|
meta["name"] = meta["name"].replace(from_prefix.title(), to_prefix.title())
|
||||||
|
meta["name"] = re.sub(
|
||||||
|
rf"\b{from_prefix}\b", to_prefix, meta["name"], flags=re.IGNORECASE
|
||||||
|
)
|
||||||
|
|
||||||
|
trigger = meta.get("trigger")
|
||||||
|
if isinstance(trigger, dict) and trigger.get("type") == "cron":
|
||||||
|
trigger["cron_expression"] = cron
|
||||||
|
|
||||||
|
for source in meta.get("context_sources") or []:
|
||||||
|
if isinstance(source, dict):
|
||||||
|
params = source.get("params")
|
||||||
|
if isinstance(params, dict) and "cadence" in params:
|
||||||
|
params["cadence"] = cadence_enum
|
||||||
|
|
||||||
|
body = body.replace(f"{from_prefix}-", f"{to_prefix}-")
|
||||||
|
body = re.sub(rf"\b{from_prefix}\b", to_prefix, body, flags=re.IGNORECASE)
|
||||||
|
|
||||||
|
dest = path.with_name(f"{to_prefix}-{stem}.md")
|
||||||
|
dest.write_text(_render_markdown(meta, body), encoding="utf-8")
|
||||||
|
if dest != path:
|
||||||
|
path.unlink()
|
||||||
|
return dest
|
||||||
|
|
||||||
|
|
||||||
|
def _promote_definitions(
|
||||||
|
engagement_repo: Path,
|
||||||
|
from_phase: str,
|
||||||
|
to_phase: str,
|
||||||
|
loop_crons: dict[str, dict[str, str]],
|
||||||
|
*,
|
||||||
|
dry_run: bool,
|
||||||
|
result: PromoteResult,
|
||||||
|
) -> None:
|
||||||
|
from_prefix = PHASE_FILE_PREFIX[from_phase]
|
||||||
|
to_prefix = PHASE_FILE_PREFIX[to_phase]
|
||||||
|
cadence_enum = CADENCE_ENUM[to_phase]
|
||||||
|
defs_dir = engagement_repo / "activity-definitions"
|
||||||
|
if not defs_dir.is_dir():
|
||||||
|
raise PromoteError(f"activity-definitions/ not found under {engagement_repo}")
|
||||||
|
|
||||||
|
for loop_id, crons in loop_crons.items():
|
||||||
|
for stem, cron in crons.items():
|
||||||
|
src = defs_dir / f"{from_prefix}-{stem}.md"
|
||||||
|
if not src.is_file():
|
||||||
|
result.errors.append(f"missing definition file: {src}")
|
||||||
|
continue
|
||||||
|
dest = defs_dir / f"{to_prefix}-{stem}.md"
|
||||||
|
result.actions.append(
|
||||||
|
PromoteAction(
|
||||||
|
layer="definitions",
|
||||||
|
description=f"{src.name} → {dest.name} (cron {cron})",
|
||||||
|
path=str(src),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if dry_run:
|
||||||
|
continue
|
||||||
|
_transform_definition(src, from_prefix, to_prefix, cron, cadence_enum)
|
||||||
|
|
||||||
|
# Update event-definition fallback references
|
||||||
|
for name in EVENT_DEFINITIONS:
|
||||||
|
path = defs_dir / f"{name}.md"
|
||||||
|
if not path.is_file():
|
||||||
|
continue
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
new_text = text.replace(
|
||||||
|
f"{from_prefix}-metrics-health-sweep",
|
||||||
|
f"{to_prefix}-metrics-health-sweep",
|
||||||
|
)
|
||||||
|
new_text = new_text.replace(
|
||||||
|
f"`0 6 * * *`",
|
||||||
|
f"`{loop_crons.get('quality-escalation', {}).get('metrics-health-sweep', '0 6 * * 1')}`",
|
||||||
|
)
|
||||||
|
if new_text != text:
|
||||||
|
result.actions.append(
|
||||||
|
PromoteAction(
|
||||||
|
layer="definitions",
|
||||||
|
description=f"update fallback reference in {path.name}",
|
||||||
|
path=str(path),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not dry_run:
|
||||||
|
path.write_text(new_text, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _promote_cadence_files(
|
||||||
|
engagement_repo: Path,
|
||||||
|
to_phase: str,
|
||||||
|
*,
|
||||||
|
loop_filter: str | None,
|
||||||
|
dry_run: bool,
|
||||||
|
result: PromoteResult,
|
||||||
|
) -> dict[str, dict[str, str]]:
|
||||||
|
if to_phase != "operate":
|
||||||
|
raise PromoteError(f"promotion to '{to_phase}' not implemented yet")
|
||||||
|
|
||||||
|
promoted_at = date.today().isoformat()
|
||||||
|
loop_crons: dict[str, dict[str, str]] = {}
|
||||||
|
for loop_id, path in _loop_paths(engagement_repo).items():
|
||||||
|
if loop_filter and loop_filter != loop_id:
|
||||||
|
continue
|
||||||
|
data = _load_yaml(path)
|
||||||
|
current = str(data.get("phase", ""))
|
||||||
|
if current == to_phase:
|
||||||
|
loop_crons[loop_id] = _operate_crons(loop_id, data)
|
||||||
|
continue
|
||||||
|
if current != "stabilize":
|
||||||
|
result.errors.append(
|
||||||
|
f"{loop_id}: cannot promote from phase '{current}' to '{to_phase}'"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
loop_crons[loop_id] = _operate_crons(loop_id, data)
|
||||||
|
result.actions.append(
|
||||||
|
PromoteAction(
|
||||||
|
layer="cadence",
|
||||||
|
description=f"{path.relative_to(engagement_repo)}: {current} → {to_phase}",
|
||||||
|
path=str(path),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if dry_run:
|
||||||
|
continue
|
||||||
|
_apply_operate_target(data, loop_id, promoted_at)
|
||||||
|
_write_yaml(path, data)
|
||||||
|
|
||||||
|
roster_path = engagement_repo / "loops" / "kaizen-stack" / "roster.yaml"
|
||||||
|
if roster_path.is_file() and (
|
||||||
|
loop_filter is None or loop_filter == "kaizen-improvement-stack"
|
||||||
|
):
|
||||||
|
roster = _load_yaml(roster_path)
|
||||||
|
if roster.get("phase") != to_phase:
|
||||||
|
result.actions.append(
|
||||||
|
PromoteAction(
|
||||||
|
layer="cadence",
|
||||||
|
description=f"roster.yaml phase → {to_phase}",
|
||||||
|
path=str(roster_path),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not dry_run:
|
||||||
|
roster["phase"] = to_phase
|
||||||
|
_write_yaml(roster_path, roster)
|
||||||
|
|
||||||
|
return loop_crons
|
||||||
|
|
||||||
|
|
||||||
|
def _sync_roster_phase(
|
||||||
|
engagement_repo: Path,
|
||||||
|
to_phase: str,
|
||||||
|
*,
|
||||||
|
dry_run: bool,
|
||||||
|
result: PromoteResult,
|
||||||
|
) -> None:
|
||||||
|
roster_path = engagement_repo / "loops" / "kaizen-stack" / "roster.yaml"
|
||||||
|
if not roster_path.is_file():
|
||||||
|
return
|
||||||
|
roster = _load_yaml(roster_path)
|
||||||
|
if roster.get("phase") == to_phase:
|
||||||
|
return
|
||||||
|
result.actions.append(
|
||||||
|
PromoteAction(
|
||||||
|
layer="cadence",
|
||||||
|
description=f"roster.yaml phase → {to_phase}",
|
||||||
|
path=str(roster_path),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not dry_run:
|
||||||
|
roster["phase"] = to_phase
|
||||||
|
_write_yaml(roster_path, roster)
|
||||||
|
|
||||||
|
|
||||||
|
def _promote_fleet(
|
||||||
|
engagement_repo: Path,
|
||||||
|
engagement_slug: str,
|
||||||
|
to_phase: str,
|
||||||
|
*,
|
||||||
|
dry_run: bool,
|
||||||
|
result: PromoteResult,
|
||||||
|
) -> None:
|
||||||
|
if to_phase != "operate":
|
||||||
|
return
|
||||||
|
_sync_roster_phase(engagement_repo, to_phase, dry_run=dry_run, result=result)
|
||||||
|
for slug, root, agents in _roster_repos(engagement_repo):
|
||||||
|
if not root.is_dir():
|
||||||
|
result.errors.append(f"fleet repo root missing: {root} ({slug})")
|
||||||
|
continue
|
||||||
|
sched = schedule_path(root)
|
||||||
|
result.actions.append(
|
||||||
|
PromoteAction(
|
||||||
|
layer="fleet",
|
||||||
|
description=f"{slug}: .kaizen/schedule.yml → weekly ({', '.join(agents)})",
|
||||||
|
path=str(sched),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if dry_run:
|
||||||
|
continue
|
||||||
|
yaml_text = engagement_schedule_yaml(
|
||||||
|
engagement_slug,
|
||||||
|
agents=agents,
|
||||||
|
bootstrap_cadence="weekly",
|
||||||
|
)
|
||||||
|
sched.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
sched.write_text(yaml_text, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _sync_activity_core(
|
||||||
|
engagement_repo: Path,
|
||||||
|
activity_core_root: Path | None,
|
||||||
|
*,
|
||||||
|
dry_run: bool,
|
||||||
|
result: PromoteResult,
|
||||||
|
) -> None:
|
||||||
|
ac_root = activity_core_root or Path(os.environ.get("ACTIVITY_CORE_ROOT", ""))
|
||||||
|
if not ac_root or not (ac_root / "src" / "activity_core").is_dir():
|
||||||
|
result.errors.append(
|
||||||
|
"activity-core root not found (set --activity-core or ACTIVITY_CORE_ROOT)"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if not os.environ.get("ACTCORE_DB_URL"):
|
||||||
|
result.errors.append("ACTCORE_DB_URL not set — skip sync or configure database")
|
||||||
|
return
|
||||||
|
|
||||||
|
cmd = ["uv", "run", "python", "-m", "activity_core.sync_activity_definitions"]
|
||||||
|
result.actions.append(
|
||||||
|
PromoteAction(
|
||||||
|
layer="sync",
|
||||||
|
description="activity-core sync_activity_definitions",
|
||||||
|
path=str(ac_root),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["ACTIVITY_DEFINITION_DIRS"] = str(engagement_repo.resolve())
|
||||||
|
subprocess.run(cmd, cwd=ac_root, env=env, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def promote_engagement(
|
||||||
|
engagement_repo: Path,
|
||||||
|
*,
|
||||||
|
engagement_slug: str = "coulomb-loop",
|
||||||
|
to_phase: str | None = None,
|
||||||
|
loop: str | None = None,
|
||||||
|
dry_run: bool = False,
|
||||||
|
skip_cadence: bool = False,
|
||||||
|
skip_definitions: bool = False,
|
||||||
|
skip_fleet: bool = False,
|
||||||
|
skip_sync: bool = False,
|
||||||
|
activity_core_root: Path | None = None,
|
||||||
|
) -> PromoteResult:
|
||||||
|
"""Run atomic cadence promotion (or dry-run plan)."""
|
||||||
|
engagement_repo = engagement_repo.resolve()
|
||||||
|
result = PromoteResult()
|
||||||
|
|
||||||
|
if not engagement_repo.is_dir():
|
||||||
|
result.errors.append(f"engagement repo not found: {engagement_repo}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Resolve target phase from first loop if not specified
|
||||||
|
if to_phase is None:
|
||||||
|
for path in _loop_paths(engagement_repo).values():
|
||||||
|
data = _load_yaml(path)
|
||||||
|
nxt = _next_phase(str(data.get("phase", "bootstrap")))
|
||||||
|
if nxt:
|
||||||
|
to_phase = nxt
|
||||||
|
break
|
||||||
|
if to_phase is None:
|
||||||
|
result.errors.append("could not infer target phase (all loops at operate?)")
|
||||||
|
return result
|
||||||
|
|
||||||
|
loop_crons: dict[str, dict[str, str]] = {}
|
||||||
|
from_phase = (
|
||||||
|
PHASE_ORDER[PHASE_ORDER.index(to_phase) - 1]
|
||||||
|
if to_phase in PHASE_ORDER[1:]
|
||||||
|
else "stabilize"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not skip_cadence:
|
||||||
|
loop_crons = _promote_cadence_files(
|
||||||
|
engagement_repo,
|
||||||
|
to_phase,
|
||||||
|
loop_filter=loop,
|
||||||
|
dry_run=dry_run,
|
||||||
|
result=result,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for loop_id, path in _loop_paths(engagement_repo).items():
|
||||||
|
if loop and loop != loop_id:
|
||||||
|
continue
|
||||||
|
loop_crons[loop_id] = _operate_crons(loop_id, _load_yaml(path))
|
||||||
|
|
||||||
|
if (
|
||||||
|
not skip_definitions
|
||||||
|
and from_phase in PHASE_FILE_PREFIX
|
||||||
|
and to_phase in PHASE_FILE_PREFIX
|
||||||
|
):
|
||||||
|
if from_phase != to_phase:
|
||||||
|
_promote_definitions(
|
||||||
|
engagement_repo,
|
||||||
|
from_phase,
|
||||||
|
to_phase,
|
||||||
|
loop_crons,
|
||||||
|
dry_run=dry_run,
|
||||||
|
result=result,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not skip_fleet:
|
||||||
|
_promote_fleet(
|
||||||
|
engagement_repo,
|
||||||
|
engagement_slug,
|
||||||
|
to_phase,
|
||||||
|
dry_run=dry_run,
|
||||||
|
result=result,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not skip_sync:
|
||||||
|
_sync_activity_core(
|
||||||
|
engagement_repo,
|
||||||
|
activity_core_root,
|
||||||
|
dry_run=dry_run,
|
||||||
|
result=result,
|
||||||
|
)
|
||||||
|
except (PromoteError, ScheduleError, subprocess.CalledProcessError) as exc:
|
||||||
|
result.errors.append(str(exc))
|
||||||
|
|
||||||
|
return result
|
||||||
@@ -6,7 +6,11 @@ from pathlib import Path
|
|||||||
from typing import List, Dict, Optional
|
from typing import List, Dict, Optional
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from .registry import AgentRegistry
|
from .registry import AgentRegistry, AgentDefinition
|
||||||
|
from .agent_docs import (
|
||||||
|
render_installed_agents_section,
|
||||||
|
upsert_installed_agents_section,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -82,7 +86,15 @@ class AgentInstaller:
|
|||||||
|
|
||||||
installed = []
|
installed = []
|
||||||
for agent_file in agents_dir.glob("agent-*.md"):
|
for agent_file in agents_dir.glob("agent-*.md"):
|
||||||
agent_name = agent_file.stem.replace("agent-", "")
|
# Prefer the frontmatter name (registry-authoritative); fall back to
|
||||||
|
# the filename when frontmatter is missing/unreadable. The filename
|
||||||
|
# encodes the category for a few agents (e.g. agent-project-
|
||||||
|
# management.md → name: project-assistant), so a pure filename derive
|
||||||
|
# produces names the registry cannot resolve (WP-0007 T02).
|
||||||
|
try:
|
||||||
|
agent_name = AgentDefinition._read_frontmatter(agent_file)["name"]
|
||||||
|
except Exception:
|
||||||
|
agent_name = agent_file.stem.replace("agent-", "")
|
||||||
installed.append(agent_name)
|
installed.append(agent_name)
|
||||||
|
|
||||||
return sorted(installed)
|
return sorted(installed)
|
||||||
@@ -235,60 +247,25 @@ agents-validate:
|
|||||||
try:
|
try:
|
||||||
claude_md = project_dir / "CLAUDE.md"
|
claude_md = project_dir / "CLAUDE.md"
|
||||||
|
|
||||||
agent_section = "## Installed Agents\n\n"
|
agents = [
|
||||||
agent_section += (
|
agent
|
||||||
"This project includes the following specialized agents:\n\n"
|
for agent in (self.registry.get_agent(name) for name in agent_names)
|
||||||
)
|
if agent is not None
|
||||||
|
]
|
||||||
|
agent_section = render_installed_agents_section(agents)
|
||||||
|
|
||||||
# Group agents by category
|
# Update or create CLAUDE.md (idempotent upsert — WP-0007 T01/T02)
|
||||||
categories = {}
|
|
||||||
for agent_name in agent_names:
|
|
||||||
agent = self.registry.get_agent(agent_name)
|
|
||||||
if agent:
|
|
||||||
category = agent.category.value
|
|
||||||
if category not in categories:
|
|
||||||
categories[category] = []
|
|
||||||
categories[category].append(agent)
|
|
||||||
|
|
||||||
# Generate documentation
|
|
||||||
for category, agents in categories.items():
|
|
||||||
agent_section += f"### {category.replace('-', ' ').title()}\n\n"
|
|
||||||
for agent in agents:
|
|
||||||
agent_section += f"- **{agent.name}**: {agent.description}\n"
|
|
||||||
agent_section += "\n"
|
|
||||||
|
|
||||||
agent_section += (
|
|
||||||
"Use these agents by referencing them in your "
|
|
||||||
"Claude Code interactions.\n\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Update or create CLAUDE.md
|
|
||||||
if claude_md.exists():
|
if claude_md.exists():
|
||||||
with open(claude_md, "r") as f:
|
content = claude_md.read_text()
|
||||||
content = f.read()
|
content = upsert_installed_agents_section(content, agent_section)
|
||||||
|
claude_md.write_text(content)
|
||||||
# Replace existing agent section or append
|
|
||||||
if "## Installed Agents" in content:
|
|
||||||
import re
|
|
||||||
|
|
||||||
content = re.sub(
|
|
||||||
r"## Installed Agents.*?(?=##|\Z)",
|
|
||||||
agent_section,
|
|
||||||
content,
|
|
||||||
flags=re.DOTALL,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
content += "\n" + agent_section
|
|
||||||
|
|
||||||
with open(claude_md, "w") as f:
|
|
||||||
f.write(content)
|
|
||||||
else:
|
else:
|
||||||
# Create new CLAUDE.md
|
|
||||||
header = "# Claude Code Configuration\n\n"
|
header = "# Claude Code Configuration\n\n"
|
||||||
header += "This file contains Claude Code configuration and agent information.\n\n"
|
header += (
|
||||||
|
"This file contains Claude Code configuration and agent "
|
||||||
with open(claude_md, "w") as f:
|
"information.\n\n"
|
||||||
f.write(header + agent_section)
|
)
|
||||||
|
claude_md.write_text(header + agent_section)
|
||||||
|
|
||||||
print(f"Updated documentation: {claude_md}")
|
print(f"Updated documentation: {claude_md}")
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
"""Ecosystem integration adapters (Helix Forge, artifact-store)."""
|
"""Ecosystem integration adapters (Helix Forge, artifact-store, event bus)."""
|
||||||
|
|
||||||
from .artifact_store import publish_optimizer_evidence
|
from .artifact_store import publish_optimizer_evidence
|
||||||
|
from .event_bus import (
|
||||||
|
build_metrics_recorded_envelope,
|
||||||
|
publish_metrics_recorded_event,
|
||||||
|
resolve_project_slug,
|
||||||
|
)
|
||||||
from .helix import HelixCorrelationAdapter, enrich_helix_correlation
|
from .helix import HelixCorrelationAdapter, enrich_helix_correlation
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"HelixCorrelationAdapter",
|
"HelixCorrelationAdapter",
|
||||||
|
"build_metrics_recorded_envelope",
|
||||||
"enrich_helix_correlation",
|
"enrich_helix_correlation",
|
||||||
|
"publish_metrics_recorded_event",
|
||||||
"publish_optimizer_evidence",
|
"publish_optimizer_evidence",
|
||||||
|
"resolve_project_slug",
|
||||||
]
|
]
|
||||||
|
|||||||
95
src/kaizen_agentic/integrations/event_bus.py
Normal file
95
src/kaizen_agentic/integrations/event_bus.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
"""NATS event emission for activity-core integration (Pattern 2)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, Mapping, Optional
|
||||||
|
|
||||||
|
EVENT_TYPE_METRICS_RECORDED = "kaizen.metrics.recorded"
|
||||||
|
DEFAULT_NATS_URL = "nats://localhost:4222"
|
||||||
|
DEFAULT_PUBLISHER = "kaizen-agentic"
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_project_slug(project_root: Path) -> str:
|
||||||
|
"""Return state-hub repo slug for a project checkout."""
|
||||||
|
override = os.environ.get("KAIZEN_PROJECT_SLUG", "").strip()
|
||||||
|
if override:
|
||||||
|
return override
|
||||||
|
return Path(project_root).resolve().name
|
||||||
|
|
||||||
|
|
||||||
|
def metrics_summary_for_event(summary: Mapping[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Map ADR-004 summary.json to the LOOP-WP-0002 event contract."""
|
||||||
|
return {
|
||||||
|
"success_rate": summary.get("success_rate", 0.0),
|
||||||
|
"execution_count": summary.get("execution_count", 0),
|
||||||
|
"avg_quality": summary.get(
|
||||||
|
"avg_quality",
|
||||||
|
summary.get("avg_quality_score", 0.0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_metrics_recorded_envelope(
|
||||||
|
*,
|
||||||
|
agent: str,
|
||||||
|
project: str,
|
||||||
|
summary: Mapping[str, Any],
|
||||||
|
event_id: Optional[str] = None,
|
||||||
|
publisher: str = DEFAULT_PUBLISHER,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Build an activity-core EventEnvelope dict for kaizen.metrics.recorded."""
|
||||||
|
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
return {
|
||||||
|
"id": event_id or str(uuid.uuid4()),
|
||||||
|
"type": EVENT_TYPE_METRICS_RECORDED,
|
||||||
|
"version": "1.0",
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"publisher": publisher,
|
||||||
|
"attributes": {
|
||||||
|
"agent": agent,
|
||||||
|
"project": project,
|
||||||
|
"summary": metrics_summary_for_event(summary),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def nats_subject_for_event(event_type: str) -> str:
|
||||||
|
"""Subject pattern used by activity-core webhook receiver and event router."""
|
||||||
|
return f"activity.{event_type}"
|
||||||
|
|
||||||
|
|
||||||
|
async def _publish_bytes(subject: str, payload: bytes, *, nats_url: str) -> None:
|
||||||
|
try:
|
||||||
|
import nats
|
||||||
|
except ImportError as exc:
|
||||||
|
raise RuntimeError(
|
||||||
|
"nats-py is required for --emit-event. "
|
||||||
|
"Install with: pip install 'kaizen-agentic[events]'"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
nc = await nats.connect(nats_url)
|
||||||
|
try:
|
||||||
|
await nc.publish(subject, payload)
|
||||||
|
await nc.flush()
|
||||||
|
finally:
|
||||||
|
await nc.close()
|
||||||
|
|
||||||
|
|
||||||
|
def publish_metrics_recorded_event(
|
||||||
|
envelope: Mapping[str, Any],
|
||||||
|
*,
|
||||||
|
nats_url: Optional[str] = None,
|
||||||
|
) -> str:
|
||||||
|
"""Publish envelope to NATS. Returns the subject used."""
|
||||||
|
url = (nats_url or os.environ.get("NATS_URL") or DEFAULT_NATS_URL).strip()
|
||||||
|
event_type = str(envelope.get("type", EVENT_TYPE_METRICS_RECORDED))
|
||||||
|
subject = nats_subject_for_event(event_type)
|
||||||
|
payload = json.dumps(envelope, sort_keys=True).encode("utf-8")
|
||||||
|
asyncio.run(_publish_bytes(subject, payload, nats_url=url))
|
||||||
|
return subject
|
||||||
@@ -60,8 +60,10 @@ class AgentDefinition:
|
|||||||
# Extract dependencies from frontmatter and content
|
# Extract dependencies from frontmatter and content
|
||||||
dependencies = cls._extract_dependencies(content, frontmatter)
|
dependencies = cls._extract_dependencies(content, frontmatter)
|
||||||
|
|
||||||
# Determine category from name or content
|
# The declared frontmatter category is authoritative when it is a known
|
||||||
category = cls._determine_category(frontmatter["name"], content)
|
# value (it is what `validate` enforces, WP-0007 T03); fall back to the
|
||||||
|
# name/content heuristic only when absent or unrecognised.
|
||||||
|
category = cls._resolve_category(frontmatter, content)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
name=frontmatter["name"],
|
name=frontmatter["name"],
|
||||||
@@ -118,6 +120,17 @@ class AgentDefinition:
|
|||||||
|
|
||||||
return dependencies
|
return dependencies
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _resolve_category(cls, frontmatter: dict, content: str) -> AgentCategory:
|
||||||
|
"""Prefer the declared frontmatter category; heuristic as fallback."""
|
||||||
|
declared = frontmatter.get("category")
|
||||||
|
if isinstance(declared, str):
|
||||||
|
try:
|
||||||
|
return AgentCategory(declared.strip())
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return cls._determine_category(frontmatter["name"], content)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _determine_category(name: str, content: str) -> AgentCategory:
|
def _determine_category(name: str, content: str) -> AgentCategory:
|
||||||
"""Determine agent category based on name and content."""
|
"""Determine agent category based on name and content."""
|
||||||
@@ -288,6 +301,65 @@ class AgentRegistry:
|
|||||||
|
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
def validate_frontmatter_schema(self) -> Dict[str, List[str]]:
|
||||||
|
"""Validate each agent file's frontmatter against the required schema.
|
||||||
|
|
||||||
|
Required: ``name``, ``description``, ``category`` (non-empty strings);
|
||||||
|
``category`` must be a known :class:`AgentCategory` value. Optional:
|
||||||
|
``memory`` ∈ {enabled, disabled}; ``model`` a non-empty string. Keyed by
|
||||||
|
filename so files with a missing/invalid ``name`` still surface.
|
||||||
|
"""
|
||||||
|
errors: Dict[str, List[str]] = {}
|
||||||
|
valid_categories = {c.value for c in AgentCategory}
|
||||||
|
|
||||||
|
if not self.agents_dir.exists():
|
||||||
|
return errors
|
||||||
|
|
||||||
|
for agent_file in sorted(self.agents_dir.glob("agent-*.md")):
|
||||||
|
file_errors: List[str] = []
|
||||||
|
try:
|
||||||
|
content = agent_file.read_text(encoding="utf-8")
|
||||||
|
match = re.match(r"^---\n(.*?)\n---\n", content, re.DOTALL)
|
||||||
|
if not match:
|
||||||
|
errors[agent_file.name] = ["missing YAML frontmatter"]
|
||||||
|
continue
|
||||||
|
frontmatter = yaml.safe_load(match.group(1))
|
||||||
|
if not isinstance(frontmatter, dict):
|
||||||
|
errors[agent_file.name] = ["frontmatter is not a mapping"]
|
||||||
|
continue
|
||||||
|
|
||||||
|
for field in ("name", "description", "category"):
|
||||||
|
value = frontmatter.get(field)
|
||||||
|
if not isinstance(value, str) or not value.strip():
|
||||||
|
file_errors.append(f"missing or empty required field: {field}")
|
||||||
|
|
||||||
|
category = frontmatter.get("category")
|
||||||
|
if isinstance(category, str) and category not in valid_categories:
|
||||||
|
file_errors.append(
|
||||||
|
f"invalid category '{category}' (expected one of "
|
||||||
|
f"{', '.join(sorted(valid_categories))})"
|
||||||
|
)
|
||||||
|
|
||||||
|
memory = frontmatter.get("memory")
|
||||||
|
if memory is not None and memory not in ("enabled", "disabled"):
|
||||||
|
file_errors.append(
|
||||||
|
f"invalid memory '{memory}' "
|
||||||
|
"(expected 'enabled' or 'disabled')"
|
||||||
|
)
|
||||||
|
|
||||||
|
model = frontmatter.get("model")
|
||||||
|
if model is not None and (
|
||||||
|
not isinstance(model, str) or not model.strip()
|
||||||
|
):
|
||||||
|
file_errors.append("model must be a non-empty string when present")
|
||||||
|
except Exception as exc: # pragma: no cover - defensive
|
||||||
|
file_errors.append(f"failed to parse frontmatter: {exc}")
|
||||||
|
|
||||||
|
if file_errors:
|
||||||
|
errors[agent_file.name] = file_errors
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
def _has_circular_dependency(
|
def _has_circular_dependency(
|
||||||
self, agent_name: str, visited: Optional[Set[str]] = None
|
self, agent_name: str, visited: Optional[Set[str]] = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|||||||
261
src/kaizen_agentic/schedule.py
Normal file
261
src/kaizen_agentic/schedule.py
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
"""Repo-local scheduled agent execution manifest (.kaizen/schedule.yml).
|
||||||
|
|
||||||
|
ADR-005 defines the schedule contract: which agents run on what cadence in an
|
||||||
|
opted-in repo. kaizen-agentic owns parsing, validation, and preparing an
|
||||||
|
orientation bundle for a scheduled run. It does **not** run cron schedules or
|
||||||
|
invoke Claude — activity-core fires the cron and a coding-agent session executes
|
||||||
|
the prepared bundle.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
SCHEDULE_RELATIVE_PATH = Path(".kaizen") / "schedule.yml"
|
||||||
|
SCHEDULE_VERSION = "1"
|
||||||
|
VALID_CADENCES = ("daily", "weekly", "monthly")
|
||||||
|
|
||||||
|
# Sensible defaults for `schedule init` — coach + optimization weekly, the
|
||||||
|
# heavier tdd-workflow review monthly and disabled until an operator opts in.
|
||||||
|
DEFAULT_AGENTS: Dict[str, Dict[str, Any]] = {
|
||||||
|
"coach": {"cadence": "weekly", "cron": "0 9 * * 1", "enabled": True},
|
||||||
|
"optimization": {"cadence": "weekly", "cron": "0 10 * * 1", "enabled": True},
|
||||||
|
"tdd-workflow": {"cadence": "monthly", "enabled": False},
|
||||||
|
}
|
||||||
|
DEFAULT_TIMEZONE = "Europe/Berlin"
|
||||||
|
DEFAULT_ENGAGEMENT_AGENTS = ("coach", "optimization")
|
||||||
|
|
||||||
|
# Bootstrap cadence presets for customer engagements (coulomb-loop ADR-003).
|
||||||
|
# Hourly bootstrap keeps cadence enum ``daily`` so activity-core definitions
|
||||||
|
# filtering ``cadence: daily`` still match while per-repo cron overrides fire
|
||||||
|
# hourly (see docs/integrations/activity-core-handoff-engagement.md).
|
||||||
|
ENGAGEMENT_CADENCE_PRESETS: Dict[str, Dict[str, Dict[str, Any]]] = {
|
||||||
|
"hourly": {
|
||||||
|
"coach": {"cadence": "daily", "cron": "15 * * * *", "enabled": True},
|
||||||
|
"optimization": {"cadence": "daily", "cron": "30 * * * *", "enabled": True},
|
||||||
|
"tdd-workflow": {"cadence": "monthly", "enabled": False},
|
||||||
|
},
|
||||||
|
"daily": {
|
||||||
|
"coach": {"cadence": "daily", "cron": "0 8 * * *", "enabled": True},
|
||||||
|
"optimization": {"cadence": "daily", "cron": "0 9 * * *", "enabled": True},
|
||||||
|
"tdd-workflow": {"cadence": "monthly", "enabled": False},
|
||||||
|
},
|
||||||
|
"weekly": {
|
||||||
|
"coach": {"cadence": "weekly", "cron": "0 9 * * 1", "enabled": True},
|
||||||
|
"optimization": {"cadence": "weekly", "cron": "0 10 * * 1", "enabled": True},
|
||||||
|
"tdd-workflow": {"cadence": "monthly", "enabled": False},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ScheduleError(Exception):
|
||||||
|
"""Raised when a schedule manifest cannot be parsed."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ScheduleEntry:
|
||||||
|
"""One scheduled agent run declaration."""
|
||||||
|
|
||||||
|
agent: str
|
||||||
|
cadence: str
|
||||||
|
enabled: bool = True
|
||||||
|
cron: Optional[str] = None
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
data: Dict[str, Any] = {"cadence": self.cadence, "enabled": self.enabled}
|
||||||
|
if self.cron:
|
||||||
|
data["cron"] = self.cron
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Schedule:
|
||||||
|
"""Parsed `.kaizen/schedule.yml` manifest."""
|
||||||
|
|
||||||
|
version: str
|
||||||
|
timezone: Optional[str] = None
|
||||||
|
entries: List[ScheduleEntry] = field(default_factory=list)
|
||||||
|
source_path: Optional[Path] = None
|
||||||
|
|
||||||
|
def entry_for(self, agent: str) -> Optional[ScheduleEntry]:
|
||||||
|
for entry in self.entries:
|
||||||
|
if entry.agent == agent:
|
||||||
|
return entry
|
||||||
|
return None
|
||||||
|
|
||||||
|
def enabled_entries(self) -> List[ScheduleEntry]:
|
||||||
|
return [e for e in self.entries if e.enabled]
|
||||||
|
|
||||||
|
|
||||||
|
def schedule_path(project_root: Path) -> Path:
|
||||||
|
"""Return the canonical schedule.yml path for a project root."""
|
||||||
|
return Path(project_root) / SCHEDULE_RELATIVE_PATH
|
||||||
|
|
||||||
|
|
||||||
|
def parse_schedule(data: Any, source_path: Optional[Path] = None) -> Schedule:
|
||||||
|
"""Parse a raw mapping into a Schedule (structural errors raise).
|
||||||
|
|
||||||
|
Semantic validation (known agents, cadence values) is handled by
|
||||||
|
:func:`validate_schedule` so callers can collect actionable error lists.
|
||||||
|
"""
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise ScheduleError("schedule.yml must be a YAML mapping at the top level")
|
||||||
|
|
||||||
|
version = data.get("version")
|
||||||
|
if version is None:
|
||||||
|
raise ScheduleError("schedule.yml is missing required key: version")
|
||||||
|
version = str(version)
|
||||||
|
|
||||||
|
timezone = data.get("timezone")
|
||||||
|
if timezone is not None and not isinstance(timezone, str):
|
||||||
|
raise ScheduleError("timezone must be a string")
|
||||||
|
|
||||||
|
agents = data.get("agents", {})
|
||||||
|
if not isinstance(agents, dict):
|
||||||
|
raise ScheduleError("agents must be a mapping of agent-name -> settings")
|
||||||
|
|
||||||
|
entries: List[ScheduleEntry] = []
|
||||||
|
for name, settings in agents.items():
|
||||||
|
if settings is None:
|
||||||
|
settings = {}
|
||||||
|
if not isinstance(settings, dict):
|
||||||
|
raise ScheduleError(f"agent '{name}' settings must be a mapping")
|
||||||
|
cron = settings.get("cron")
|
||||||
|
if cron is not None and not isinstance(cron, str):
|
||||||
|
raise ScheduleError(f"agent '{name}' cron must be a string")
|
||||||
|
entries.append(
|
||||||
|
ScheduleEntry(
|
||||||
|
agent=str(name),
|
||||||
|
cadence=str(settings.get("cadence", "")),
|
||||||
|
enabled=bool(settings.get("enabled", True)),
|
||||||
|
cron=cron,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return Schedule(
|
||||||
|
version=version,
|
||||||
|
timezone=timezone,
|
||||||
|
entries=entries,
|
||||||
|
source_path=source_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_schedule(path: Path) -> Schedule:
|
||||||
|
"""Load and parse a schedule.yml file (raises ScheduleError)."""
|
||||||
|
path = Path(path)
|
||||||
|
if not path.exists():
|
||||||
|
raise ScheduleError(f"schedule file not found: {path}")
|
||||||
|
try:
|
||||||
|
raw = yaml.safe_load(path.read_text(encoding="utf-8"))
|
||||||
|
except yaml.YAMLError as exc: # pragma: no cover - passthrough message
|
||||||
|
raise ScheduleError(f"invalid YAML in {path}: {exc}") from exc
|
||||||
|
return parse_schedule(raw, source_path=path)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_schedule(
|
||||||
|
schedule: Schedule, known_agents: Optional[List[str]] = None
|
||||||
|
) -> List[str]:
|
||||||
|
"""Return a list of human-readable validation errors (empty == valid)."""
|
||||||
|
errors: List[str] = []
|
||||||
|
|
||||||
|
if schedule.version != SCHEDULE_VERSION:
|
||||||
|
errors.append(
|
||||||
|
f"unsupported version '{schedule.version}' "
|
||||||
|
f"(expected '{SCHEDULE_VERSION}')"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not schedule.entries:
|
||||||
|
errors.append("no agents declared under 'agents:'")
|
||||||
|
|
||||||
|
seen: set = set()
|
||||||
|
known = set(known_agents) if known_agents is not None else None
|
||||||
|
for entry in schedule.entries:
|
||||||
|
if entry.agent in seen:
|
||||||
|
errors.append(f"duplicate agent entry: {entry.agent}")
|
||||||
|
seen.add(entry.agent)
|
||||||
|
|
||||||
|
if entry.cadence not in VALID_CADENCES:
|
||||||
|
errors.append(
|
||||||
|
f"agent '{entry.agent}': invalid cadence '{entry.cadence}' "
|
||||||
|
f"(expected one of {', '.join(VALID_CADENCES)})"
|
||||||
|
)
|
||||||
|
|
||||||
|
if known is not None and entry.agent not in known:
|
||||||
|
errors.append(
|
||||||
|
f"agent '{entry.agent}' is not an installed or packaged agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
def default_schedule_yaml(timezone: str = DEFAULT_TIMEZONE) -> str:
|
||||||
|
"""Render the default schedule.yml scaffold for `schedule init`."""
|
||||||
|
document = {
|
||||||
|
"version": SCHEDULE_VERSION,
|
||||||
|
"timezone": timezone,
|
||||||
|
"agents": dict(DEFAULT_AGENTS),
|
||||||
|
}
|
||||||
|
header = (
|
||||||
|
"# Kaizen scheduled agent execution manifest (ADR-005)\n"
|
||||||
|
"# Declares which agents run on what cadence in this repo.\n"
|
||||||
|
"# Validate with: kaizen-agentic schedule validate\n"
|
||||||
|
)
|
||||||
|
body = yaml.safe_dump(document, sort_keys=False, default_flow_style=False)
|
||||||
|
return header + body
|
||||||
|
|
||||||
|
|
||||||
|
def engagement_schedule_yaml(
|
||||||
|
engagement: str,
|
||||||
|
*,
|
||||||
|
agents: Optional[List[str]] = None,
|
||||||
|
bootstrap_cadence: str = "hourly",
|
||||||
|
timezone: str = DEFAULT_TIMEZONE,
|
||||||
|
) -> str:
|
||||||
|
"""Render a customer-engagement bootstrap schedule for `schedule init --engagement`."""
|
||||||
|
if bootstrap_cadence not in ENGAGEMENT_CADENCE_PRESETS:
|
||||||
|
raise ScheduleError(
|
||||||
|
f"unsupported bootstrap cadence '{bootstrap_cadence}' "
|
||||||
|
f"(expected one of {', '.join(ENGAGEMENT_CADENCE_PRESETS)})"
|
||||||
|
)
|
||||||
|
|
||||||
|
slug = engagement.strip()
|
||||||
|
if not slug:
|
||||||
|
raise ScheduleError("engagement slug must not be empty")
|
||||||
|
|
||||||
|
selected = list(agents or DEFAULT_ENGAGEMENT_AGENTS)
|
||||||
|
if not selected:
|
||||||
|
raise ScheduleError(
|
||||||
|
"at least one agent is required for engagement schedule init"
|
||||||
|
)
|
||||||
|
|
||||||
|
preset = ENGAGEMENT_CADENCE_PRESETS[bootstrap_cadence]
|
||||||
|
agent_entries: Dict[str, Dict[str, Any]] = {}
|
||||||
|
for name in selected:
|
||||||
|
if name not in preset:
|
||||||
|
raise ScheduleError(
|
||||||
|
f"agent '{name}' has no preset for bootstrap cadence '{bootstrap_cadence}'"
|
||||||
|
)
|
||||||
|
agent_entries[name] = dict(preset[name])
|
||||||
|
|
||||||
|
if bootstrap_cadence == "hourly":
|
||||||
|
cadence_note = "hourly crons, daily cadence enum"
|
||||||
|
else:
|
||||||
|
cadence_note = f"{bootstrap_cadence} cadence"
|
||||||
|
|
||||||
|
document = {
|
||||||
|
"version": SCHEDULE_VERSION,
|
||||||
|
"timezone": timezone,
|
||||||
|
"agents": agent_entries,
|
||||||
|
}
|
||||||
|
header = (
|
||||||
|
"# Kaizen scheduled agent execution manifest (ADR-005)\n"
|
||||||
|
f"# Engagement: {slug} bootstrap — {cadence_note}\n"
|
||||||
|
"# Regulator promotes cadence per customer engagement policy (ADR-003).\n"
|
||||||
|
"# Validate with: kaizen-agentic schedule validate\n"
|
||||||
|
)
|
||||||
|
body = yaml.safe_dump(document, sort_keys=False, default_flow_style=False)
|
||||||
|
return header + body
|
||||||
97
tests/test_agent_docs.py
Normal file
97
tests/test_agent_docs.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
"""Tests for idempotent agent-docs generation (WP-0007 T01/T02)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from click.testing import CliRunner
|
||||||
|
|
||||||
|
from kaizen_agentic.agent_docs import (
|
||||||
|
SECTION_FOOTER,
|
||||||
|
SECTION_HEADING,
|
||||||
|
render_installed_agents_section,
|
||||||
|
upsert_installed_agents_section,
|
||||||
|
)
|
||||||
|
from kaizen_agentic.cli import cli
|
||||||
|
from kaizen_agentic.registry import AgentRegistry
|
||||||
|
|
||||||
|
AGENTS_DIR = Path(__file__).parent.parent / "agents"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def runner() -> CliRunner:
|
||||||
|
return CliRunner()
|
||||||
|
|
||||||
|
|
||||||
|
def _section() -> str:
|
||||||
|
agents = AgentRegistry(AGENTS_DIR).list_agents()[:4]
|
||||||
|
return render_installed_agents_section(agents)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUpsertIdempotency:
|
||||||
|
def test_upsert_is_idempotent(self):
|
||||||
|
section = _section()
|
||||||
|
base = f"# Project\n\nintro\n\n{section}\n## Keep Me\nbody\n"
|
||||||
|
once = upsert_installed_agents_section(base, section)
|
||||||
|
twice = upsert_installed_agents_section(once, section)
|
||||||
|
assert once == twice
|
||||||
|
assert once.count(SECTION_HEADING) == 1
|
||||||
|
assert once.count(SECTION_FOOTER) == 1
|
||||||
|
# A following top-level section must survive the replace
|
||||||
|
assert once.count("## Keep Me") == 1
|
||||||
|
|
||||||
|
def test_subheadings_do_not_truncate_section(self):
|
||||||
|
section = _section()
|
||||||
|
# The block contains '### Category' subheadings; the replace must not
|
||||||
|
# stop at the first one (the original regex bug).
|
||||||
|
merged = upsert_installed_agents_section("# P\n\n", section)
|
||||||
|
assert merged.count(SECTION_FOOTER) == 1
|
||||||
|
assert "### " in merged # categories rendered
|
||||||
|
|
||||||
|
def test_append_when_absent(self):
|
||||||
|
section = _section()
|
||||||
|
merged = upsert_installed_agents_section("# Project\n\nbody\n", section)
|
||||||
|
assert SECTION_HEADING in merged
|
||||||
|
assert merged.count(SECTION_FOOTER) == 1
|
||||||
|
|
||||||
|
|
||||||
|
class TestDocsGenerateCli:
|
||||||
|
def test_generate_then_check_clean(self, runner: CliRunner, tmp_path: Path):
|
||||||
|
# A project with two installed agents
|
||||||
|
proj = tmp_path / "proj"
|
||||||
|
(proj / "agents").mkdir(parents=True)
|
||||||
|
for name, cat in (("alpha", "testing"), ("beta", "code-quality")):
|
||||||
|
(proj / "agents" / f"agent-{name}.md").write_text(
|
||||||
|
f"---\nname: {name}\ndescription: d{name}\ncategory: {cat}\n---\nx\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
gen = runner.invoke(cli, ["docs", "generate", "--target", str(proj)])
|
||||||
|
assert gen.exit_code == 0
|
||||||
|
claude = (proj / "CLAUDE.md").read_text()
|
||||||
|
assert claude.count(SECTION_HEADING) == 1
|
||||||
|
assert "**alpha**" in claude and "**beta**" in claude
|
||||||
|
|
||||||
|
# Second generate is a no-op
|
||||||
|
again = runner.invoke(cli, ["docs", "generate", "--target", str(proj)])
|
||||||
|
assert "already up to date" in again.output
|
||||||
|
|
||||||
|
# --check passes on a synced repo
|
||||||
|
check = runner.invoke(
|
||||||
|
cli, ["docs", "generate", "--check", "--target", str(proj)]
|
||||||
|
)
|
||||||
|
assert check.exit_code == 0
|
||||||
|
assert "up to date" in check.output
|
||||||
|
|
||||||
|
def test_check_fails_when_stale(self, runner: CliRunner, tmp_path: Path):
|
||||||
|
proj = tmp_path / "proj"
|
||||||
|
(proj / "agents").mkdir(parents=True)
|
||||||
|
(proj / "agents" / "agent-alpha.md").write_text(
|
||||||
|
"---\nname: alpha\ndescription: d\ncategory: testing\n---\nx\n"
|
||||||
|
)
|
||||||
|
(proj / "CLAUDE.md").write_text("# Proj\n\nno agents section yet\n")
|
||||||
|
check = runner.invoke(
|
||||||
|
cli, ["docs", "generate", "--check", "--target", str(proj)]
|
||||||
|
)
|
||||||
|
assert check.exit_code == 1
|
||||||
|
assert "out of date" in check.output
|
||||||
@@ -40,10 +40,15 @@ class TestClickWorkaround:
|
|||||||
assert "Got unexpected extra argument" not in stdout_content
|
assert "Got unexpected extra argument" not in stdout_content
|
||||||
assert "Got unexpected extra argument" not in stderr_content
|
assert "Got unexpected extra argument" not in stderr_content
|
||||||
|
|
||||||
def test_update_command_error_suppression(self):
|
def test_update_command_error_suppression(self, tmp_path):
|
||||||
"""Test that spurious 'unexpected extra argument' errors are suppressed for update commands."""
|
"""Test that spurious 'unexpected extra argument' errors are suppressed for update commands."""
|
||||||
|
# Seed a temp project so `update` does not rewrite the repo's own agents/
|
||||||
|
(tmp_path / "agents").mkdir()
|
||||||
|
(tmp_path / "agents" / "agent-tdd-workflow.md").write_text(
|
||||||
|
"---\nname: tdd-workflow\ndescription: d\ncategory: testing\n---\nx\n"
|
||||||
|
)
|
||||||
# Test the update command that also shows spurious errors
|
# Test the update command that also shows spurious errors
|
||||||
with patch("sys.argv", ["kaizen-agentic", "update"]):
|
with patch("sys.argv", ["kaizen-agentic", "update", "--target", str(tmp_path)]):
|
||||||
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
|
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
|
||||||
with patch("sys.stderr", new_callable=StringIO) as mock_stderr:
|
with patch("sys.stderr", new_callable=StringIO) as mock_stderr:
|
||||||
try:
|
try:
|
||||||
@@ -116,9 +121,12 @@ class TestClickWorkaround:
|
|||||||
class TestInstallCommandSpecifics:
|
class TestInstallCommandSpecifics:
|
||||||
"""Test specific install command scenarios."""
|
"""Test specific install command scenarios."""
|
||||||
|
|
||||||
def test_install_with_valid_agent(self):
|
def test_install_with_valid_agent(self, tmp_path):
|
||||||
"""Test install command with a valid agent name."""
|
"""Test install command with a valid agent name."""
|
||||||
with patch("sys.argv", ["kaizen-agentic", "install", "tdd-workflow"]):
|
with patch(
|
||||||
|
"sys.argv",
|
||||||
|
["kaizen-agentic", "install", "tdd-workflow", "--target", str(tmp_path)],
|
||||||
|
):
|
||||||
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
|
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
|
||||||
with patch("sys.stderr", new_callable=StringIO) as mock_stderr:
|
with patch("sys.stderr", new_callable=StringIO) as mock_stderr:
|
||||||
try:
|
try:
|
||||||
|
|||||||
100
tests/test_create_agent.py
Normal file
100
tests/test_create_agent.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
"""Tests for the create-agent scaffold (WP-0007 T04)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from click.testing import CliRunner
|
||||||
|
|
||||||
|
from kaizen_agentic.cli import cli
|
||||||
|
from kaizen_agentic.registry import AgentRegistry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def runner() -> CliRunner:
|
||||||
|
return CliRunner()
|
||||||
|
|
||||||
|
|
||||||
|
class TestCreateAgent:
|
||||||
|
def test_scaffold_is_schema_valid_and_loads(
|
||||||
|
self, runner: CliRunner, tmp_path: Path
|
||||||
|
):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"create-agent",
|
||||||
|
"demo-helper",
|
||||||
|
"-c",
|
||||||
|
"testing",
|
||||||
|
"-d",
|
||||||
|
"Demo helper for tests",
|
||||||
|
"--target",
|
||||||
|
str(tmp_path),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
agent_path = tmp_path / "agents" / "agent-demo-helper.md"
|
||||||
|
assert agent_path.exists()
|
||||||
|
|
||||||
|
registry = AgentRegistry(tmp_path / "agents")
|
||||||
|
# Passes the schema and is loadable by the registry.
|
||||||
|
assert registry.validate_frontmatter_schema() == {}
|
||||||
|
agent = registry.get_agent("demo-helper")
|
||||||
|
assert agent is not None
|
||||||
|
assert agent.category.value == "testing"
|
||||||
|
assert agent.memory == "enabled"
|
||||||
|
|
||||||
|
def test_interactive_prompts_when_flags_missing(
|
||||||
|
self, runner: CliRunner, tmp_path: Path
|
||||||
|
):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
["create-agent", "prompted", "--target", str(tmp_path)],
|
||||||
|
input="testing\nA prompted agent\n",
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
content = (tmp_path / "agents" / "agent-prompted.md").read_text()
|
||||||
|
assert "category: testing" in content
|
||||||
|
assert "description: A prompted agent" in content
|
||||||
|
|
||||||
|
def test_refuses_overwrite_without_force(self, runner: CliRunner, tmp_path: Path):
|
||||||
|
args = [
|
||||||
|
"create-agent",
|
||||||
|
"dup",
|
||||||
|
"-c",
|
||||||
|
"meta",
|
||||||
|
"-d",
|
||||||
|
"first",
|
||||||
|
"--target",
|
||||||
|
str(tmp_path),
|
||||||
|
]
|
||||||
|
assert runner.invoke(cli, args).exit_code == 0
|
||||||
|
second = runner.invoke(cli, args)
|
||||||
|
assert second.exit_code == 1
|
||||||
|
assert "already exists" in second.output
|
||||||
|
|
||||||
|
def test_force_overwrites(self, runner: CliRunner, tmp_path: Path):
|
||||||
|
base = ["create-agent", "dup", "--target", str(tmp_path)]
|
||||||
|
runner.invoke(cli, base + ["-c", "meta", "-d", "first"])
|
||||||
|
result = runner.invoke(cli, base + ["-c", "meta", "-d", "second", "--force"])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert (
|
||||||
|
"description: second" in (tmp_path / "agents" / "agent-dup.md").read_text()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rejects_invalid_category(self, runner: CliRunner, tmp_path: Path):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"create-agent",
|
||||||
|
"x",
|
||||||
|
"-c",
|
||||||
|
"nonsense",
|
||||||
|
"-d",
|
||||||
|
"d",
|
||||||
|
"--target",
|
||||||
|
str(tmp_path),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code != 0
|
||||||
161
tests/test_engagement_promote.py
Normal file
161
tests/test_engagement_promote.py
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
"""Tests for atomic engagement cadence promotion."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from click.testing import CliRunner
|
||||||
|
|
||||||
|
from kaizen_agentic.cli import cli
|
||||||
|
from kaizen_agentic.engagement_promote import promote_engagement
|
||||||
|
from kaizen_agentic.schedule import schedule_path
|
||||||
|
|
||||||
|
|
||||||
|
def _write_cadence(
|
||||||
|
repo: Path,
|
||||||
|
loop_dir: str,
|
||||||
|
loop_id: str,
|
||||||
|
phase: str,
|
||||||
|
*,
|
||||||
|
with_operate_target: bool = True,
|
||||||
|
) -> None:
|
||||||
|
data: dict = {"loop": loop_id, "phase": phase, "regulator_approval": "approved"}
|
||||||
|
if phase == "stabilize" and with_operate_target:
|
||||||
|
if loop_id == "kaizen-improvement-stack":
|
||||||
|
data["operate_target"] = {
|
||||||
|
"phase": "operate",
|
||||||
|
"chain": {
|
||||||
|
"metrics": "0 8 * * 1",
|
||||||
|
"coach": "0 9 * * 1",
|
||||||
|
"optimization": "0 10 * * 1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
elif loop_id == "registry-hygiene":
|
||||||
|
data["operate_target"] = {
|
||||||
|
"phase": "operate",
|
||||||
|
"cron": "0 9 * * 1",
|
||||||
|
"batch_size": 2,
|
||||||
|
}
|
||||||
|
path = repo / "loops" / loop_dir / "cadence.yml"
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _write_definition(repo: Path, name: str, cron: str, cadence: str) -> None:
|
||||||
|
defs_dir = repo / "activity-definitions"
|
||||||
|
defs_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
stem = name.removeprefix("daily-")
|
||||||
|
content = f"""---
|
||||||
|
id: coulomb-daily-{stem}
|
||||||
|
name: Daily test {stem}
|
||||||
|
enabled: true
|
||||||
|
trigger:
|
||||||
|
type: cron
|
||||||
|
cron_expression: "{cron}"
|
||||||
|
context_sources:
|
||||||
|
- type: kaizen
|
||||||
|
query: discover_kaizen_scheduled_repos
|
||||||
|
params:
|
||||||
|
cadence: {cadence}
|
||||||
|
---
|
||||||
|
|
||||||
|
# Daily {stem}
|
||||||
|
"""
|
||||||
|
(repo / "activity-definitions" / name).write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _engagement_fixture(tmp_path: Path) -> Path:
|
||||||
|
repo = tmp_path / "coulomb-loop"
|
||||||
|
repo.mkdir()
|
||||||
|
_write_cadence(repo, "kaizen-stack", "kaizen-improvement-stack", "stabilize")
|
||||||
|
_write_cadence(repo, "registry-hygiene", "registry-hygiene", "stabilize")
|
||||||
|
_write_definition(repo, "daily-coach-orientation.md", "0 9 * * *", "daily")
|
||||||
|
_write_definition(repo, "daily-metrics-optimize.md", "0 8 * * *", "daily")
|
||||||
|
_write_definition(repo, "daily-optimization-review.md", "0 10 * * *", "daily")
|
||||||
|
_write_definition(repo, "daily-registry-hygiene-sweep.md", "0 7 * * *", "daily")
|
||||||
|
|
||||||
|
roster = {
|
||||||
|
"version": "1",
|
||||||
|
"loop": "kaizen-improvement-stack",
|
||||||
|
"phase": "bootstrap",
|
||||||
|
"active": [
|
||||||
|
{
|
||||||
|
"slug": "pilot-a",
|
||||||
|
"root": str(tmp_path / "pilot-a"),
|
||||||
|
"agents": ["coach", "optimization"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
roster_path = repo / "loops" / "kaizen-stack" / "roster.yaml"
|
||||||
|
roster_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
roster_path.write_text(yaml.safe_dump(roster, sort_keys=False), encoding="utf-8")
|
||||||
|
(tmp_path / "pilot-a").mkdir()
|
||||||
|
return repo
|
||||||
|
|
||||||
|
|
||||||
|
class TestEngagementPromote:
|
||||||
|
def test_dry_run_lists_all_layers(self, tmp_path: Path) -> None:
|
||||||
|
repo = _engagement_fixture(tmp_path)
|
||||||
|
result = promote_engagement(
|
||||||
|
repo,
|
||||||
|
to_phase="operate",
|
||||||
|
dry_run=True,
|
||||||
|
skip_sync=True,
|
||||||
|
)
|
||||||
|
layers = {a.layer for a in result.actions}
|
||||||
|
assert "cadence" in layers
|
||||||
|
assert "definitions" in layers
|
||||||
|
assert "fleet" in layers
|
||||||
|
assert result.ok
|
||||||
|
|
||||||
|
def test_promote_updates_cadence_and_definitions(self, tmp_path: Path) -> None:
|
||||||
|
repo = _engagement_fixture(tmp_path)
|
||||||
|
result = promote_engagement(
|
||||||
|
repo,
|
||||||
|
to_phase="operate",
|
||||||
|
skip_fleet=True,
|
||||||
|
skip_sync=True,
|
||||||
|
)
|
||||||
|
assert result.ok
|
||||||
|
cadence = yaml.safe_load(
|
||||||
|
(repo / "loops" / "kaizen-stack" / "cadence.yml").read_text()
|
||||||
|
)
|
||||||
|
assert cadence["phase"] == "operate"
|
||||||
|
assert "operate_target" not in cadence
|
||||||
|
assert (repo / "activity-definitions" / "weekly-coach-orientation.md").is_file()
|
||||||
|
assert not (
|
||||||
|
repo / "activity-definitions" / "daily-coach-orientation.md"
|
||||||
|
).is_file()
|
||||||
|
|
||||||
|
def test_fleet_only_writes_schedule(self, tmp_path: Path) -> None:
|
||||||
|
repo = _engagement_fixture(tmp_path)
|
||||||
|
# Pre-promote cadence so fleet-only path applies
|
||||||
|
promote_engagement(repo, to_phase="operate", skip_fleet=True, skip_sync=True)
|
||||||
|
result = promote_engagement(
|
||||||
|
repo,
|
||||||
|
to_phase="operate",
|
||||||
|
skip_cadence=True,
|
||||||
|
skip_definitions=True,
|
||||||
|
skip_sync=True,
|
||||||
|
)
|
||||||
|
sched = schedule_path(tmp_path / "pilot-a")
|
||||||
|
assert sched.is_file()
|
||||||
|
assert "cadence: weekly" in sched.read_text()
|
||||||
|
|
||||||
|
def test_cli_fleet_only(self, tmp_path: Path) -> None:
|
||||||
|
repo = _engagement_fixture(tmp_path)
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"schedule",
|
||||||
|
"promote",
|
||||||
|
"--engagement-repo",
|
||||||
|
str(repo),
|
||||||
|
"--fleet-only",
|
||||||
|
"--skip-sync",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
assert "[fleet]" in result.output
|
||||||
@@ -13,7 +13,8 @@ DEFINITIONS_DIR = (
|
|||||||
|
|
||||||
def test_activity_definitions_have_required_frontmatter():
|
def test_activity_definitions_have_required_frontmatter():
|
||||||
files = list(DEFINITIONS_DIR.glob("*.md"))
|
files = list(DEFINITIONS_DIR.glob("*.md"))
|
||||||
assert len(files) == 3
|
# 3 from WP-0004 (metrics) + 2 from WP-0006 (scheduled agent runs)
|
||||||
|
assert len(files) == 5
|
||||||
|
|
||||||
for path in files:
|
for path in files:
|
||||||
text = path.read_text(encoding="utf-8")
|
text = path.read_text(encoding="utf-8")
|
||||||
|
|||||||
188
tests/test_metrics_emit_event.py
Normal file
188
tests/test_metrics_emit_event.py
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
"""Tests for kaizen.metrics.recorded event emission."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from click.testing import CliRunner
|
||||||
|
|
||||||
|
from kaizen_agentic.cli import cli
|
||||||
|
from kaizen_agentic.integrations.event_bus import (
|
||||||
|
EVENT_TYPE_METRICS_RECORDED,
|
||||||
|
build_metrics_recorded_envelope,
|
||||||
|
metrics_summary_for_event,
|
||||||
|
nats_subject_for_event,
|
||||||
|
publish_metrics_recorded_event,
|
||||||
|
resolve_project_slug,
|
||||||
|
)
|
||||||
|
from kaizen_agentic.metrics import MetricsStore
|
||||||
|
|
||||||
|
|
||||||
|
def test_metrics_summary_for_event_maps_avg_quality_score() -> None:
|
||||||
|
summary = metrics_summary_for_event(
|
||||||
|
{
|
||||||
|
"success_rate": 0.75,
|
||||||
|
"execution_count": 12,
|
||||||
|
"avg_quality_score": 0.81,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert summary == {
|
||||||
|
"success_rate": 0.75,
|
||||||
|
"execution_count": 12,
|
||||||
|
"avg_quality": 0.81,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_metrics_recorded_envelope_shape() -> None:
|
||||||
|
envelope = build_metrics_recorded_envelope(
|
||||||
|
agent="coach",
|
||||||
|
project="kaizen-agentic",
|
||||||
|
summary={
|
||||||
|
"success_rate": 0.9,
|
||||||
|
"execution_count": 5,
|
||||||
|
"avg_quality_score": 0.85,
|
||||||
|
},
|
||||||
|
event_id="test-event-id",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert envelope["id"] == "test-event-id"
|
||||||
|
assert envelope["type"] == EVENT_TYPE_METRICS_RECORDED
|
||||||
|
assert envelope["publisher"] == "kaizen-agentic"
|
||||||
|
assert envelope["attributes"] == {
|
||||||
|
"agent": "coach",
|
||||||
|
"project": "kaizen-agentic",
|
||||||
|
"summary": {
|
||||||
|
"success_rate": 0.9,
|
||||||
|
"execution_count": 5,
|
||||||
|
"avg_quality": 0.85,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_nats_subject_for_event() -> None:
|
||||||
|
assert nats_subject_for_event("kaizen.metrics.recorded") == (
|
||||||
|
"activity.kaizen.metrics.recorded"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_project_slug_prefers_env(
|
||||||
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.setenv("KAIZEN_PROJECT_SLUG", "custom-slug")
|
||||||
|
assert resolve_project_slug(tmp_path / "some-dir") == "custom-slug"
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_project_slug_falls_back_to_directory_name(tmp_path: Path) -> None:
|
||||||
|
project = tmp_path / "kaizen-agentic"
|
||||||
|
project.mkdir()
|
||||||
|
assert resolve_project_slug(project) == "kaizen-agentic"
|
||||||
|
|
||||||
|
|
||||||
|
def test_publish_metrics_recorded_event_uses_activity_subject(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
published: dict[str, object] = {}
|
||||||
|
|
||||||
|
async def fake_publish(subject: str, payload: bytes, *, nats_url: str) -> None:
|
||||||
|
published["subject"] = subject
|
||||||
|
published["payload"] = payload
|
||||||
|
published["url"] = nats_url
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"kaizen_agentic.integrations.event_bus._publish_bytes",
|
||||||
|
fake_publish,
|
||||||
|
)
|
||||||
|
|
||||||
|
envelope = build_metrics_recorded_envelope(
|
||||||
|
agent="coach",
|
||||||
|
project="activity-core",
|
||||||
|
summary={"success_rate": 1.0, "execution_count": 1, "avg_quality_score": 1.0},
|
||||||
|
event_id="evt-1",
|
||||||
|
)
|
||||||
|
subject = publish_metrics_recorded_event(
|
||||||
|
envelope, nats_url="nats://broker.test:4222"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert subject == "activity.kaizen.metrics.recorded"
|
||||||
|
assert published["subject"] == "activity.kaizen.metrics.recorded"
|
||||||
|
body = json.loads(published["payload"].decode())
|
||||||
|
assert body["attributes"]["project"] == "activity-core"
|
||||||
|
|
||||||
|
|
||||||
|
def test_metrics_record_emit_event_after_append(
|
||||||
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
emitted: list[dict] = []
|
||||||
|
|
||||||
|
def capture(envelope, *, nats_url=None):
|
||||||
|
emitted.append(dict(envelope))
|
||||||
|
return "activity.kaizen.metrics.recorded"
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"kaizen_agentic.cli.publish_metrics_recorded_event",
|
||||||
|
capture,
|
||||||
|
)
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"metrics",
|
||||||
|
"record",
|
||||||
|
"coach",
|
||||||
|
"--target",
|
||||||
|
str(tmp_path),
|
||||||
|
"--success",
|
||||||
|
"--time",
|
||||||
|
"120",
|
||||||
|
"--quality",
|
||||||
|
"0.9",
|
||||||
|
"--emit-event",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
assert "Recorded metrics for 'coach'" in result.output
|
||||||
|
assert "Emitted kaizen.metrics.recorded" in result.output
|
||||||
|
assert len(emitted) == 1
|
||||||
|
assert emitted[0]["attributes"]["agent"] == "coach"
|
||||||
|
assert emitted[0]["attributes"]["project"] == tmp_path.name
|
||||||
|
assert emitted[0]["attributes"]["summary"]["execution_count"] == 1
|
||||||
|
|
||||||
|
store = MetricsStore(tmp_path, "coach")
|
||||||
|
assert store.read_summary() is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_metrics_record_skips_emit_on_idempotency_duplicate(
|
||||||
|
tmp_path: Path,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
emitted: list[dict] = []
|
||||||
|
|
||||||
|
def capture(envelope, *, nats_url=None):
|
||||||
|
emitted.append(dict(envelope))
|
||||||
|
return "activity.kaizen.metrics.recorded"
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"kaizen_agentic.cli.publish_metrics_recorded_event",
|
||||||
|
capture,
|
||||||
|
)
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
common = [
|
||||||
|
"metrics",
|
||||||
|
"record",
|
||||||
|
"coach",
|
||||||
|
"--target",
|
||||||
|
str(tmp_path),
|
||||||
|
"--success",
|
||||||
|
"--emit-event",
|
||||||
|
"--idempotency-key",
|
||||||
|
"session-1",
|
||||||
|
]
|
||||||
|
assert runner.invoke(cli, common).exit_code == 0
|
||||||
|
assert runner.invoke(cli, common).exit_code == 0
|
||||||
|
assert len(emitted) == 1
|
||||||
243
tests/test_schedule_cli.py
Normal file
243
tests/test_schedule_cli.py
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
"""CLI + module tests for scheduled agent execution (ADR-005, WP-0006)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import yaml
|
||||||
|
from click.testing import CliRunner
|
||||||
|
|
||||||
|
from kaizen_agentic.cli import cli
|
||||||
|
from kaizen_agentic.schedule import (
|
||||||
|
ScheduleError,
|
||||||
|
engagement_schedule_yaml,
|
||||||
|
parse_schedule,
|
||||||
|
schedule_path,
|
||||||
|
validate_schedule,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def runner() -> CliRunner:
|
||||||
|
return CliRunner()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def project_dir(tmp_path: Path) -> Path:
|
||||||
|
root = tmp_path / "demo-project"
|
||||||
|
root.mkdir()
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
class TestScheduleModule:
|
||||||
|
def test_engagement_schedule_yaml_hourly_preset(self):
|
||||||
|
text = engagement_schedule_yaml(
|
||||||
|
"coulomb-loop",
|
||||||
|
agents=["coach", "optimization"],
|
||||||
|
bootstrap_cadence="hourly",
|
||||||
|
)
|
||||||
|
assert "Engagement: coulomb-loop bootstrap" in text
|
||||||
|
body = "\n".join(line for line in text.splitlines() if not line.startswith("#"))
|
||||||
|
schedule = parse_schedule(yaml.safe_load(body))
|
||||||
|
coach = schedule.entry_for("coach")
|
||||||
|
assert coach is not None
|
||||||
|
assert coach.cadence == "daily"
|
||||||
|
assert coach.cron == "15 * * * *"
|
||||||
|
|
||||||
|
def test_parse_requires_version(self):
|
||||||
|
with pytest.raises(ScheduleError):
|
||||||
|
parse_schedule({"agents": {}})
|
||||||
|
|
||||||
|
def test_parse_rejects_non_mapping(self):
|
||||||
|
with pytest.raises(ScheduleError):
|
||||||
|
parse_schedule(["not", "a", "mapping"])
|
||||||
|
|
||||||
|
def test_validate_flags_unknown_agent_and_bad_cadence(self):
|
||||||
|
schedule = parse_schedule(
|
||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"agents": {
|
||||||
|
"coach": {"cadence": "weekly", "enabled": True},
|
||||||
|
"made-up": {"cadence": "hourly"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
errors = validate_schedule(schedule, known_agents=["coach", "optimization"])
|
||||||
|
assert any("hourly" in e for e in errors)
|
||||||
|
assert any("made-up" in e for e in errors)
|
||||||
|
|
||||||
|
def test_validate_clean_schedule(self):
|
||||||
|
schedule = parse_schedule(
|
||||||
|
{"version": "1", "agents": {"coach": {"cadence": "weekly"}}}
|
||||||
|
)
|
||||||
|
assert validate_schedule(schedule, known_agents=["coach"]) == []
|
||||||
|
|
||||||
|
|
||||||
|
class TestScheduleCli:
|
||||||
|
def test_init_creates_default_schedule(self, runner: CliRunner, project_dir: Path):
|
||||||
|
result = runner.invoke(cli, ["schedule", "init", "--target", str(project_dir)])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
path = schedule_path(project_dir)
|
||||||
|
assert path.exists()
|
||||||
|
assert "coach" in path.read_text()
|
||||||
|
|
||||||
|
def test_engagement_init_hourly_bootstrap(
|
||||||
|
self, runner: CliRunner, project_dir: Path
|
||||||
|
):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"schedule",
|
||||||
|
"init",
|
||||||
|
"--target",
|
||||||
|
str(project_dir),
|
||||||
|
"--engagement",
|
||||||
|
"coulomb-loop",
|
||||||
|
"--agents",
|
||||||
|
"coach,optimization",
|
||||||
|
"--bootstrap-cadence",
|
||||||
|
"hourly",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
text = schedule_path(project_dir).read_text()
|
||||||
|
assert "Engagement: coulomb-loop bootstrap" in text
|
||||||
|
assert "cron: 15 * * * *" in text
|
||||||
|
assert "cadence: daily" in text
|
||||||
|
assert "Engagement: coulomb-loop" in result.output
|
||||||
|
|
||||||
|
def test_engagement_init_validates_unknown_agent(
|
||||||
|
self, runner: CliRunner, project_dir: Path
|
||||||
|
):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"schedule",
|
||||||
|
"init",
|
||||||
|
"--target",
|
||||||
|
str(project_dir),
|
||||||
|
"--engagement",
|
||||||
|
"demo",
|
||||||
|
"--agents",
|
||||||
|
"not-a-real-agent",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert "unknown agent" in result.output
|
||||||
|
|
||||||
|
def test_engagement_flags_require_engagement_slug(
|
||||||
|
self, runner: CliRunner, project_dir: Path
|
||||||
|
):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"schedule",
|
||||||
|
"init",
|
||||||
|
"--target",
|
||||||
|
str(project_dir),
|
||||||
|
"--agents",
|
||||||
|
"coach",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert "--engagement" in result.output
|
||||||
|
|
||||||
|
def test_init_no_overwrite_without_force(
|
||||||
|
self, runner: CliRunner, project_dir: Path
|
||||||
|
):
|
||||||
|
runner.invoke(cli, ["schedule", "init", "--target", str(project_dir)])
|
||||||
|
result = runner.invoke(cli, ["schedule", "init", "--target", str(project_dir)])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "already exists" in result.output
|
||||||
|
|
||||||
|
def test_validate_passes_on_default(self, runner: CliRunner, project_dir: Path):
|
||||||
|
runner.invoke(cli, ["schedule", "init", "--target", str(project_dir)])
|
||||||
|
result = runner.invoke(
|
||||||
|
cli, ["schedule", "validate", "--target", str(project_dir)]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "valid" in result.output
|
||||||
|
|
||||||
|
def test_validate_missing_file_errors(self, runner: CliRunner, project_dir: Path):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli, ["schedule", "validate", "--target", str(project_dir)]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
|
||||||
|
def test_validate_rejects_bad_schema(self, runner: CliRunner, project_dir: Path):
|
||||||
|
path = schedule_path(project_dir)
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text("version: '1'\nagents:\n not-an-agent:\n cadence: weekly\n")
|
||||||
|
result = runner.invoke(
|
||||||
|
cli, ["schedule", "validate", "--target", str(project_dir)]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert "not-an-agent" in result.output
|
||||||
|
|
||||||
|
def test_list_shows_enabled(self, runner: CliRunner, project_dir: Path):
|
||||||
|
runner.invoke(cli, ["schedule", "init", "--target", str(project_dir)])
|
||||||
|
result = runner.invoke(cli, ["schedule", "list", "--target", str(project_dir)])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "coach" in result.output
|
||||||
|
# tdd-workflow is disabled by default; hidden without --all
|
||||||
|
assert "tdd-workflow" not in result.output
|
||||||
|
|
||||||
|
def test_list_all_shows_disabled(self, runner: CliRunner, project_dir: Path):
|
||||||
|
runner.invoke(cli, ["schedule", "init", "--target", str(project_dir)])
|
||||||
|
result = runner.invoke(
|
||||||
|
cli, ["schedule", "list", "--all", "--target", str(project_dir)]
|
||||||
|
)
|
||||||
|
assert "tdd-workflow" in result.output
|
||||||
|
|
||||||
|
def test_prepare_markdown_includes_agent_prompt(
|
||||||
|
self, runner: CliRunner, project_dir: Path
|
||||||
|
):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli, ["schedule", "prepare", "coach", "--target", str(project_dir)]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "Scheduled Run Orientation: coach" in result.output
|
||||||
|
assert "## Agent Prompt" in result.output
|
||||||
|
assert "Coach Agent" in result.output
|
||||||
|
assert "## Session Close" in result.output
|
||||||
|
|
||||||
|
def test_prepare_json_format(self, runner: CliRunner, project_dir: Path):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"schedule",
|
||||||
|
"prepare",
|
||||||
|
"coach",
|
||||||
|
"--target",
|
||||||
|
str(project_dir),
|
||||||
|
"--format",
|
||||||
|
"json",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
payload = json.loads(result.output)
|
||||||
|
assert payload["agent"] == "coach"
|
||||||
|
assert payload["agent_prompt_found"] is True
|
||||||
|
assert payload["session_close"]
|
||||||
|
|
||||||
|
def test_prepare_unknown_agent_notes_missing(
|
||||||
|
self, runner: CliRunner, project_dir: Path
|
||||||
|
):
|
||||||
|
result = runner.invoke(
|
||||||
|
cli,
|
||||||
|
["schedule", "prepare", "no-such-agent", "--target", str(project_dir)],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "not found in registry" in result.output
|
||||||
|
|
||||||
|
def test_prepare_includes_memory_when_present(
|
||||||
|
self, runner: CliRunner, project_dir: Path
|
||||||
|
):
|
||||||
|
runner.invoke(cli, ["memory", "init", "coach", "--target", str(project_dir)])
|
||||||
|
result = runner.invoke(
|
||||||
|
cli, ["schedule", "prepare", "coach", "--target", str(project_dir)]
|
||||||
|
)
|
||||||
|
assert "## Project Memory" in result.output
|
||||||
|
assert "Project Context" in result.output
|
||||||
74
tests/test_validate_schema.py
Normal file
74
tests/test_validate_schema.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
"""Tests for agent frontmatter schema validation (WP-0007 T03)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from kaizen_agentic.registry import AgentRegistry
|
||||||
|
|
||||||
|
REPO_AGENTS = Path(__file__).parent.parent / "agents"
|
||||||
|
|
||||||
|
|
||||||
|
def _registry(tmp_path: Path, files: dict) -> AgentRegistry:
|
||||||
|
agents = tmp_path / "agents"
|
||||||
|
agents.mkdir(parents=True)
|
||||||
|
for filename, content in files.items():
|
||||||
|
(agents / filename).write_text(content)
|
||||||
|
return AgentRegistry(agents)
|
||||||
|
|
||||||
|
|
||||||
|
class TestFrontmatterSchema:
|
||||||
|
def test_repo_agents_are_schema_valid(self):
|
||||||
|
# The shipped agents/ must always pass the schema.
|
||||||
|
assert AgentRegistry(REPO_AGENTS).validate_frontmatter_schema() == {}
|
||||||
|
|
||||||
|
def test_good_agent_passes(self, tmp_path: Path):
|
||||||
|
reg = _registry(
|
||||||
|
tmp_path,
|
||||||
|
{
|
||||||
|
"agent-good.md": (
|
||||||
|
"---\nname: good\ndescription: A fine agent\n"
|
||||||
|
"category: testing\nmemory: enabled\n---\nbody\n"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert reg.validate_frontmatter_schema() == {}
|
||||||
|
|
||||||
|
def test_missing_required_fields(self, tmp_path: Path):
|
||||||
|
reg = _registry(
|
||||||
|
tmp_path,
|
||||||
|
{"agent-x.md": "---\nname: x\ncategory: testing\n---\nbody\n"},
|
||||||
|
)
|
||||||
|
errors = reg.validate_frontmatter_schema()["agent-x.md"]
|
||||||
|
assert any("description" in e for e in errors)
|
||||||
|
|
||||||
|
def test_invalid_category(self, tmp_path: Path):
|
||||||
|
reg = _registry(
|
||||||
|
tmp_path,
|
||||||
|
{
|
||||||
|
"agent-x.md": (
|
||||||
|
"---\nname: x\ndescription: d\ncategory: nonsense\n---\nb\n"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
errors = reg.validate_frontmatter_schema()["agent-x.md"]
|
||||||
|
assert any("invalid category" in e for e in errors)
|
||||||
|
|
||||||
|
def test_invalid_memory(self, tmp_path: Path):
|
||||||
|
reg = _registry(
|
||||||
|
tmp_path,
|
||||||
|
{
|
||||||
|
"agent-x.md": (
|
||||||
|
"---\nname: x\ndescription: d\ncategory: testing\n"
|
||||||
|
"memory: maybe\n---\nb\n"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
errors = reg.validate_frontmatter_schema()["agent-x.md"]
|
||||||
|
assert any("invalid memory" in e for e in errors)
|
||||||
|
|
||||||
|
def test_missing_frontmatter(self, tmp_path: Path):
|
||||||
|
reg = _registry(tmp_path, {"agent-x.md": "just text, no frontmatter\n"})
|
||||||
|
assert reg.validate_frontmatter_schema()["agent-x.md"] == [
|
||||||
|
"missing YAML frontmatter"
|
||||||
|
]
|
||||||
@@ -96,7 +96,31 @@ ActivityDefinition reference copies (sync into activity-core to activate):
|
|||||||
- [post-install-metrics-scaffold](../docs/integrations/activity-definitions/post-install-metrics-scaffold.md)
|
- [post-install-metrics-scaffold](../docs/integrations/activity-definitions/post-install-metrics-scaffold.md)
|
||||||
- [low-success-rate-review](../docs/integrations/activity-definitions/low-success-rate-review.md)
|
- [low-success-rate-review](../docs/integrations/activity-definitions/low-success-rate-review.md)
|
||||||
|
|
||||||
**Workplan:** KAIZEN-WP-0004 Part 2. Patterns: [docs/INTEGRATION_PATTERNS.md](../docs/INTEGRATION_PATTERNS.md).
|
**Scheduled agent execution (WP-0006, ADR-005)** — run agents (not just the
|
||||||
|
metrics optimizer) on a cadence against a preselected repo roster:
|
||||||
|
|
||||||
|
- [weekly-coach-orientation](../docs/integrations/activity-definitions/weekly-coach-orientation.md)
|
||||||
|
- [weekly-optimization-review](../docs/integrations/activity-definitions/weekly-optimization-review.md)
|
||||||
|
- Resolver spec: [discover-kaizen-scheduled-repos](../docs/integrations/discover-kaizen-scheduled-repos.md)
|
||||||
|
- Roster fields: [state-hub-roster-fields](../docs/integrations/state-hub-roster-fields.md)
|
||||||
|
- Handoff checklist: [activity-core-handoff-wp0006](../docs/integrations/activity-core-handoff-wp0006.md)
|
||||||
|
|
||||||
|
A repo opts in by committing `.kaizen/schedule.yml` (`kaizen-agentic schedule
|
||||||
|
init`); activity-core fires the cron and creates a task per `(repo, agent)` that
|
||||||
|
runs `kaizen-agentic schedule prepare <agent>`.
|
||||||
|
|
||||||
|
**Workplan:** KAIZEN-WP-0004 Part 2 + KAIZEN-WP-0006. Patterns: [docs/INTEGRATION_PATTERNS.md](../docs/INTEGRATION_PATTERNS.md).
|
||||||
|
|
||||||
|
**Customer engagement (WP-0008, ADR-006)** — a customer repo (e.g. coulomb-loop)
|
||||||
|
orchestrates loops across a fleet roster; supplier ships playbook + CLI:
|
||||||
|
|
||||||
|
- [customer-engagement-playbook](../docs/integrations/customer-engagement-playbook.md)
|
||||||
|
- [customer-engagement-repo-layout](../docs/integrations/customer-engagement-repo-layout.md)
|
||||||
|
- [ADR-006](../docs/adr/ADR-006-customer-engagement-convention.md)
|
||||||
|
- CLI: `schedule init --engagement`, `metrics record --emit-event`
|
||||||
|
- activity-core handoff: [activity-core-handoff-engagement](../docs/integrations/activity-core-handoff-engagement.md)
|
||||||
|
|
||||||
|
Reference customer: coulomb-loop `INTENT.md` (coulomb_social domain).
|
||||||
|
|
||||||
### artifact-store (P1)
|
### artifact-store (P1)
|
||||||
|
|
||||||
@@ -194,4 +218,4 @@ WP-0001 T04. Assess before depending on it.
|
|||||||
Persisted in `history/`:
|
Persisted in `history/`:
|
||||||
|
|
||||||
- `2026-06-16-intent-gap-analysis.md`
|
- `2026-06-16-intent-gap-analysis.md`
|
||||||
- `2026-06-16-ecosystem-assessment.md`
|
- `2026-06-16-ecosystem-assessment.md`
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
id: KAIZEN-WP-0001
|
id: KAIZEN-WP-0001
|
||||||
type: workplan
|
type: workplan
|
||||||
title: "Community Engagement and Advanced Automation (v1.1.0)"
|
title: "Community Engagement and Advanced Automation (v1.1.0)"
|
||||||
domain: custodian
|
domain: agents
|
||||||
repo: kaizen-agentic
|
repo: kaizen-agentic
|
||||||
status: completed
|
status: completed
|
||||||
owner: kaizen-agentic
|
owner: kaizen-agentic
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
id: KAIZEN-WP-0002
|
id: KAIZEN-WP-0002
|
||||||
type: workplan
|
type: workplan
|
||||||
title: "Agency Framework: Project Memory, Coaching, and sys-medic Integration"
|
title: "Agency Framework: Project Memory, Coaching, and sys-medic Integration"
|
||||||
domain: custodian
|
domain: agents
|
||||||
repo: kaizen-agentic
|
repo: kaizen-agentic
|
||||||
status: done
|
status: done
|
||||||
owner: kaizen-agentic
|
owner: kaizen-agentic
|
||||||
|
|||||||
@@ -2,14 +2,111 @@
|
|||||||
id: KAIZEN-WP-0003
|
id: KAIZEN-WP-0003
|
||||||
type: workplan
|
type: workplan
|
||||||
title: "Measurement Loop: Metrics Convention, Collection, and Optimizer Integration"
|
title: "Measurement Loop: Metrics Convention, Collection, and Optimizer Integration"
|
||||||
domain: custodian
|
domain: agents
|
||||||
repo: kaizen-agentic
|
repo: kaizen-agentic
|
||||||
status: completed
|
status: completed
|
||||||
owner: kaizen-agentic
|
owner: kaizen-agentic
|
||||||
topic_slug: custodian
|
topic_slug: custodian
|
||||||
state_hub_workstream_id: 36252a45-f360-4496-bf77-17b5dfb02767
|
state_hub_workstream_id: 36252a45-f360-4496-bf77-17b5dfb02767
|
||||||
created: "2026-06-16"
|
created: "2026-06-16"
|
||||||
updated: "2026-06-18"
|
updated: "2026-06-17"
|
||||||
|
tasks:
|
||||||
|
- id: T01
|
||||||
|
title: Write ADR-004 project metrics convention
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 4e7b0fd2-38c0-46aa-84a7-bb18366b8c7c
|
||||||
|
- id: T02
|
||||||
|
title: Implement MetricsStore in metrics.py
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: eeaa99c7-d7a7-403b-a013-364cba45a663
|
||||||
|
- id: T03
|
||||||
|
title: Add memory init hook to scaffold metrics directory
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 247c097f-de89-4383-930c-35ee66de9b36
|
||||||
|
- id: T04
|
||||||
|
title: Unit tests for MetricsStore
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 3aa14026-6ee3-4384-b409-11300c1302f0
|
||||||
|
- id: T05
|
||||||
|
title: Implement metrics CLI command group
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 6b505d29-7d2e-44a2-a4b7-1fe82884390c
|
||||||
|
- id: T06
|
||||||
|
title: Integrate metrics record into session-close template
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 84f2a357-f2dd-4fc7-96b6-a4e80d5467a7
|
||||||
|
- id: T07
|
||||||
|
title: CLI tests for metrics commands
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 8e9ee64b-b7c4-4dff-ac6e-988fd47ef95d
|
||||||
|
- id: T08
|
||||||
|
title: Update CLI cheat sheet and agency-framework with metrics
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 4c41e0db-d5d8-4a1b-b346-06ad004edf4a
|
||||||
|
- id: T09
|
||||||
|
title: Add OptimizationLoop.from_metrics_store factory
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 0b374439-6eca-4754-8e15-2a7eece0cd27
|
||||||
|
- id: T10
|
||||||
|
title: Implement kaizen-agentic metrics optimize command
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: db87a09b-0252-495c-a771-a43b4b98f820
|
||||||
|
- id: T11
|
||||||
|
title: Consolidate optimization meta-agent definitions
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 73cb7d73-6fc6-42a9-97aa-d33cdf9ee363
|
||||||
|
- id: T12
|
||||||
|
title: Update optimization agent session protocol
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: c127eca7-7394-42db-ba5e-721aef0ccb76
|
||||||
|
- id: T13
|
||||||
|
title: Unit and integration tests for optimizer recommendations
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: f208dc9f-cdf7-47e3-9c03-09097e46eee9
|
||||||
|
- id: T14
|
||||||
|
title: Extend memory brief with metrics summary
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: d01f969c-bbb1-4eca-a4f1-d79d5c867b35
|
||||||
|
- id: T15
|
||||||
|
title: Extend agent-coach.md for metrics context
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 67f791a4-fced-4986-a331-7eb4ea47fe6e
|
||||||
|
- id: T16
|
||||||
|
title: E2e test memory brief with metrics sections
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 1fb89b54-8bd2-40bf-9a71-04693cb9f695
|
||||||
|
- id: T17
|
||||||
|
title: Add metrics section to agent-tdd-workflow.md
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 1d471a7a-9a98-4805-903e-b4a2b8153717
|
||||||
|
- id: T18
|
||||||
|
title: Add session-close metrics record step to tdd-workflow
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: abb387f1-86ce-4b9b-a516-2d4efb6aca4c
|
||||||
|
- id: T19
|
||||||
|
title: Document tdd-workflow pilot in wiki/AboutKaizenAgents.md
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 67fbc26e-a57d-4133-96e6-3d2cdbd10dc0
|
||||||
|
- id: T20
|
||||||
|
title: E2e test full tdd-workflow measure-analyse-orient loop
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: fbdd7c8b-e122-48d9-8c8f-de9f82d025e3
|
||||||
|
- id: T21
|
||||||
|
title: Sync 4 missing agents into packaged data/
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 9662bcec-34fe-451b-b61f-5d11b9574576
|
||||||
|
- id: T22
|
||||||
|
title: Update README orientation links to INTENT and wiki
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 422aae43-5697-4a00-86e9-1569baf09422
|
||||||
|
- id: T23
|
||||||
|
title: Update architecture.md agent table
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: ba6b3411-d330-4a58-8cd0-62b4fbef8c5f
|
||||||
|
- id: T24
|
||||||
|
title: CHANGELOG entry for metrics convention and CLI
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 748be9f3-f6ac-4f26-a844-6330268935b6
|
||||||
---
|
---
|
||||||
|
|
||||||
# KAIZEN-WP-0003 — Measurement Loop: Metrics Convention, Collection, and Optimizer Integration
|
# KAIZEN-WP-0003 — Measurement Loop: Metrics Convention, Collection, and Optimizer Integration
|
||||||
@@ -249,6 +346,23 @@ A reader of `INTENT.md` can point to this repo and say:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Completion
|
||||||
|
|
||||||
|
**Shipped:** v1.1.0 (2026-06-18); measurement loop operational through v1.2.0.
|
||||||
|
|
||||||
|
| Milestone | Detail |
|
||||||
|
|-----------|--------|
|
||||||
|
| ADR-004 | Project metrics convention (`.kaizen/metrics/`) |
|
||||||
|
| CLI | `metrics record`, `show`, `list`, `export`, `optimize` |
|
||||||
|
| Coach bridge | `memory brief` includes `## Performance Summary` |
|
||||||
|
| Pilot | `tdd-workflow` reference in `wiki/AboutKaizenAgents.md` |
|
||||||
|
| Tests | `test_metrics*.py`, `test_optimization_metrics.py`, e2e agency tests pass |
|
||||||
|
|
||||||
|
All 24 tasks complete. Fleet-wide template conformance and scheduled optimizer runs
|
||||||
|
remain out of scope (future workplans).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## State Hub Task IDs
|
## State Hub Task IDs
|
||||||
|
|
||||||
| Code | UUID |
|
| Code | UUID |
|
||||||
@@ -286,4 +400,4 @@ A reader of `INTENT.md` can point to this repo and say:
|
|||||||
|
|
||||||
- Retention default: 180 days (per `wiki/AgentKaizenOptimizer.md`); override via project config in a later iteration
|
- Retention default: 180 days (per `wiki/AgentKaizenOptimizer.md`); override via project config in a later iteration
|
||||||
- WP-0001 T04 (telemetry) should consume ADR-004 schema rather than inventing a parallel format
|
- WP-0001 T04 (telemetry) should consume ADR-004 schema rather than inventing a parallel format
|
||||||
- `OptimizationLoop` threshold constants (30s execution, 0.8 success rate) are starting points; expose in config later
|
- `OptimizationLoop` threshold constants (30s execution, 0.8 success rate) are starting points; expose in config later
|
||||||
|
|||||||
@@ -2,14 +2,91 @@
|
|||||||
id: KAIZEN-WP-0004
|
id: KAIZEN-WP-0004
|
||||||
type: workplan
|
type: workplan
|
||||||
title: "Ecosystem Integration: Helix Forge, activity-core, and artifact-store"
|
title: "Ecosystem Integration: Helix Forge, activity-core, and artifact-store"
|
||||||
domain: custodian
|
domain: agents
|
||||||
repo: kaizen-agentic
|
repo: kaizen-agentic
|
||||||
status: completed
|
status: completed
|
||||||
owner: kaizen-agentic
|
owner: kaizen-agentic
|
||||||
topic_slug: custodian
|
topic_slug: custodian
|
||||||
state_hub_workstream_id: 76be7294-e201-4074-91c0-6421992470fe
|
state_hub_workstream_id: 76be7294-e201-4074-91c0-6421992470fe
|
||||||
created: "2026-06-16"
|
created: "2026-06-16"
|
||||||
updated: "2026-06-18"
|
updated: "2026-06-17"
|
||||||
|
tasks:
|
||||||
|
- id: T01
|
||||||
|
title: Document Helix Forge correlation contract cross-repo
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: f365d19e-9619-4453-bebf-f1fd596b1bd1
|
||||||
|
- id: T02
|
||||||
|
title: Add HELIX_SESSION_UID to metrics record
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: e7f47683-5957-49db-bcbd-3aa47f44a073
|
||||||
|
- id: T03
|
||||||
|
title: Add kaizen-agentic metrics correlate command stub
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 6ef8ba99-7d0c-44f4-835d-7a66e9d55984
|
||||||
|
- id: T04
|
||||||
|
title: Integration test for helix_session_uid correlation
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 9875422c-a54b-40f1-a444-6b485a9e57d6
|
||||||
|
- id: T05
|
||||||
|
title: Update EcosystemIntegration.md with worked example
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 0dc33d13-0e0b-4336-a7ad-371fc533b823
|
||||||
|
- id: T06
|
||||||
|
title: Draft weekly metrics optimize ActivityDefinition
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: dbaa5f46-f66a-4a74-b4a0-97978e47d1c3
|
||||||
|
- id: T07
|
||||||
|
title: Draft post-install metrics scaffold ActivityDefinition
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 161a264a-8f70-4e37-a854-bd5a76a0e54b
|
||||||
|
- id: T08
|
||||||
|
title: Draft low success_rate review ActivityDefinition
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 3b58ad38-839c-436a-8d97-ef5a8f9beefe
|
||||||
|
- id: T09
|
||||||
|
title: Document activity-core triggers in INTEGRATION_PATTERNS.md
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: a004b60f-4e8f-4881-b088-229ac9ab242f
|
||||||
|
- id: T10
|
||||||
|
title: Smoke test activity-core activation
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 84866bf1-5830-470d-87a5-9786222332c2
|
||||||
|
- id: T11
|
||||||
|
title: Define optimizer artifact package manifest
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 033a19db-fbd2-411f-9d2e-779d210400d4
|
||||||
|
- id: T12
|
||||||
|
title: Add kaizen-agentic metrics publish command
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 54517f2b-23e3-433b-a483-c59227625dbc
|
||||||
|
- id: T13
|
||||||
|
title: Map raw-evidence retention class in publish manifest
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 3b378789-a761-4472-b072-a346541be239
|
||||||
|
- id: T14
|
||||||
|
title: Integration test artifact-store publish
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: a3566713-db58-4519-b9c4-5003421c1f1e
|
||||||
|
- id: T15
|
||||||
|
title: Document publish workflow in agency-framework.md
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 5d8255aa-fd7a-4fe6-bce2-3a176f954c7f
|
||||||
|
- id: T16
|
||||||
|
title: Map KaizenAgentTemplate to info-tech-canon profile outline
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 852c9cbf-0b0c-4f23-8594-905ca280c268
|
||||||
|
- id: T17
|
||||||
|
title: Draft info-tech-canon agent brief for tdd-workflow
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 62e05097-9033-401d-bbe0-d5d773da50fe
|
||||||
|
- id: T18
|
||||||
|
title: Spike kontextual-engine wiki ingestion manifest
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: cd6962c7-aaed-4d7d-81de-37c0e3ed715e
|
||||||
|
- id: T19
|
||||||
|
title: Update ecosystem assessment with Part 4 outcomes
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 2c1f66f5-e6ab-4e19-88ca-818acb15a706
|
||||||
---
|
---
|
||||||
|
|
||||||
# KAIZEN-WP-0004 — Ecosystem Integration: Helix Forge, activity-core, and artifact-store
|
# KAIZEN-WP-0004 — Ecosystem Integration: Helix Forge, activity-core, and artifact-store
|
||||||
@@ -154,6 +231,21 @@ Estimated effort: 3–5 sessions after WP-0003 Part 3.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Completion
|
||||||
|
|
||||||
|
**Shipped:** v1.1.0–v1.2.0 ecosystem integration layer.
|
||||||
|
|
||||||
|
| Part | Outcome |
|
||||||
|
|------|---------|
|
||||||
|
| 1 Helix | `HELIX_SESSION_UID` on `metrics record`, `metrics correlate`, bidirectional docs |
|
||||||
|
| 2 activity-core | Three ActivityDefinitions under `docs/integrations/activity-definitions/` |
|
||||||
|
| 3 artifact-store | `metrics publish` with `raw-evidence` retention manifest |
|
||||||
|
| 4 Canon/knowledge | Design docs for info-tech-canon and kontextual-engine (no runtime deps) |
|
||||||
|
|
||||||
|
All 19 tasks complete. Reciprocal Helix link verified in WP-0005 T16.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## State Hub Task IDs
|
## State Hub Task IDs
|
||||||
|
|
||||||
| Code | UUID |
|
| Code | UUID |
|
||||||
@@ -187,4 +279,4 @@ Estimated effort: 3–5 sessions after WP-0003 Part 3.
|
|||||||
- ADR-004 Helix Forge correlation section is the authoritative field mapping
|
- ADR-004 Helix Forge correlation section is the authoritative field mapping
|
||||||
- WP-0001 T04 (telemetry) should evaluate tele-mcp as adapter candidate
|
- WP-0001 T04 (telemetry) should evaluate tele-mcp as adapter candidate
|
||||||
- activity-core ActivityDefinitions live in activity-core repo per ACT-ADR-002/003;
|
- activity-core ActivityDefinitions live in activity-core repo per ACT-ADR-002/003;
|
||||||
kaizen-agentic commits reference copies or links under `docs/integrations/`
|
kaizen-agentic commits reference copies or links under `docs/integrations/`
|
||||||
|
|||||||
@@ -2,14 +2,79 @@
|
|||||||
id: KAIZEN-WP-0005
|
id: KAIZEN-WP-0005
|
||||||
type: workplan
|
type: workplan
|
||||||
title: "Adoption Polish and Fleet Parity (v1.2.0)"
|
title: "Adoption Polish and Fleet Parity (v1.2.0)"
|
||||||
domain: custodian
|
domain: agents
|
||||||
repo: kaizen-agentic
|
repo: kaizen-agentic
|
||||||
status: completed
|
status: completed
|
||||||
owner: kaizen-agentic
|
owner: kaizen-agentic
|
||||||
topic_slug: custodian
|
topic_slug: custodian
|
||||||
state_hub_workstream_id: 88c7b3e6-be98-480c-b47b-936e74a1a31b
|
state_hub_workstream_id: 88c7b3e6-be98-480c-b47b-936e74a1a31b
|
||||||
created: "2026-06-16"
|
created: "2026-06-16"
|
||||||
updated: "2026-06-16"
|
updated: "2026-06-17"
|
||||||
|
tasks:
|
||||||
|
- id: T01
|
||||||
|
title: Configure PACKAGE_USER and PACKAGE_TOKEN in Gitea
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 1fb6b04e-0854-4cc9-83c7-5abf85fe5bff
|
||||||
|
- id: T02
|
||||||
|
title: Smoke-test publish-python-package workflow
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: bdb9e463-bdfb-405c-afc4-e93a7d58a18b
|
||||||
|
- id: T03
|
||||||
|
title: Add pre-tag release checklist to PACKAGE_RELEASE.md
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 6a2132e7-8b3f-4960-a5e8-85bad81e8b13
|
||||||
|
- id: T04
|
||||||
|
title: Update HELLO_WORLD_TUTORIAL.md Gitea install paths
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 553cba3a-dafa-483a-9200-70ac3f5eb2d7
|
||||||
|
- id: T05
|
||||||
|
title: Update CLI_CHEAT_SHEET.md Gitea install paths
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 7e6663a5-fedf-4b1c-acd9-6df6b43d8a12
|
||||||
|
- id: T06
|
||||||
|
title: Update AGENT_DISTRIBUTION.md Gitea install paths
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: fe553788-357f-45c4-8400-f764f68c1cde
|
||||||
|
- id: T07
|
||||||
|
title: Update Makefile agents-* install fallback messages
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: b131ff67-fe21-4d95-904b-6a0b916c5502
|
||||||
|
- id: T08
|
||||||
|
title: Cross-link CONTRIBUTING.md with PACKAGE_RELEASE.md
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 07dd4d25-250c-455c-8363-49269d2ee59f
|
||||||
|
- id: T09
|
||||||
|
title: Add make agents-sync-package target
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 7437cedd-5f7e-4c4d-9142-4f67470c9e52
|
||||||
|
- id: T10
|
||||||
|
title: Add release-check parity test agents vs data/agents
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: ddbe2114-7a47-48fd-a145-b22dca2b581a
|
||||||
|
- id: T11
|
||||||
|
title: Refresh SCOPE.md agent-count notes
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 5417524f-03c0-40ed-a48b-a7906e6daf8f
|
||||||
|
- id: T12
|
||||||
|
title: Refresh TODO.md for v1.2.0
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 5cfad56c-2664-4b2d-b5f9-4792c958c9a2
|
||||||
|
- id: T13
|
||||||
|
title: Open CHANGELOG Unreleased for v1.2.0
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: fbca9be4-3d2b-4989-baaf-97e6926bdc66
|
||||||
|
- id: T14
|
||||||
|
title: Add flake8 to Gitea CI workflow
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: a6966cfa-ca59-4087-8989-2870dc69b13f
|
||||||
|
- id: T15
|
||||||
|
title: Document activity-core ActivityDefinition registration handoff
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: fbf3f1a8-4818-473e-ae0d-cd80118e5319
|
||||||
|
- id: T16
|
||||||
|
title: Verify bidirectional Helix correlation doc link
|
||||||
|
status: done
|
||||||
|
state_hub_task_id: 37679ce7-dcb6-42a4-820d-cf8b32c2a248
|
||||||
---
|
---
|
||||||
|
|
||||||
# KAIZEN-WP-0005 — Adoption Polish and Fleet Parity
|
# KAIZEN-WP-0005 — Adoption Polish and Fleet Parity
|
||||||
@@ -154,6 +219,23 @@ Estimated effort: 2–4 sessions.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Completion
|
||||||
|
|
||||||
|
**Shipped:** v1.2.0 on 2026-06-16
|
||||||
|
|
||||||
|
| Milestone | Detail |
|
||||||
|
|-----------|--------|
|
||||||
|
| Release commit | `297afed` — adoption polish and publish pipeline |
|
||||||
|
| Tag | `v1.2.0` pushed to Gitea |
|
||||||
|
| Publish | Gitea Actions run **3044** — `kaizen_agentic-1.2.0` wheel + sdist uploaded |
|
||||||
|
| Publish auth | `PACKAGE_USER=tegwick`, token from OpenBao `inter-hub-pkg-rep` |
|
||||||
|
| Helix handoff | Reciprocal link in `agentic-resources/docs/DESIGN-session-memory.md` §11 |
|
||||||
|
|
||||||
|
All 16 tasks complete. Deferred items (agent wizard, template validation, doc generation)
|
||||||
|
tracked for **WP-0006** (v1.3.0).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## State Hub Task IDs
|
## State Hub Task IDs
|
||||||
|
|
||||||
| Code | UUID |
|
| Code | UUID |
|
||||||
|
|||||||
366
workplans/kaizen-agentic-WP-0006-scheduled-agent-execution.md
Normal file
366
workplans/kaizen-agentic-WP-0006-scheduled-agent-execution.md
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
---
|
||||||
|
id: KAIZEN-WP-0006
|
||||||
|
type: workplan
|
||||||
|
title: "Scheduled Agent Execution via activity-core (v1.3.0)"
|
||||||
|
domain: agents
|
||||||
|
repo: kaizen-agentic
|
||||||
|
status: done
|
||||||
|
owner: kaizen-agentic
|
||||||
|
topic_slug: custodian
|
||||||
|
state_hub_workstream_id: d4edb92b-526f-4ee4-8efe-a9fa84ab6dff
|
||||||
|
depends_on:
|
||||||
|
- KAIZEN-WP-0003
|
||||||
|
- KAIZEN-WP-0004
|
||||||
|
created: "2026-06-17"
|
||||||
|
updated: "2026-06-17"
|
||||||
|
tasks:
|
||||||
|
- id: T01
|
||||||
|
state_hub_task_id: f513cf74-6e32-4dce-a3bc-2995f98a30de
|
||||||
|
status: done
|
||||||
|
title: Write ADR-005 scheduled agent execution contract
|
||||||
|
- id: T02
|
||||||
|
state_hub_task_id: dcb8d674-5472-4657-85bc-02ca9890fb92
|
||||||
|
status: done
|
||||||
|
title: Define .kaizen/schedule.yml schema and example
|
||||||
|
- id: T03
|
||||||
|
state_hub_task_id: 1f9a4959-d200-4d7b-8666-96472e23d820
|
||||||
|
status: done
|
||||||
|
title: Add kaizen-agentic schedule validate command
|
||||||
|
- id: T04
|
||||||
|
state_hub_task_id: 13ab4b76-ded2-4613-b8e2-15ab206d8c57
|
||||||
|
status: done
|
||||||
|
title: Document fleet roster and repo opt-in in INTEGRATION_PATTERNS
|
||||||
|
- id: T05
|
||||||
|
state_hub_task_id: 685b35f3-af20-483a-9136-150f5fff68dd
|
||||||
|
status: done
|
||||||
|
title: Draft state-hub roster query design for activity-core resolver
|
||||||
|
- id: T06
|
||||||
|
state_hub_task_id: 5f0a0859-3ff5-4ec8-b916-35486f045b2b
|
||||||
|
status: done
|
||||||
|
title: Draft discover_kaizen_scheduled_repos context resolver spec
|
||||||
|
- id: T07
|
||||||
|
state_hub_task_id: 2fd0f00c-5cf1-47bd-9c12-aee65bca5932
|
||||||
|
status: done
|
||||||
|
title: Draft weekly-agent-run ActivityDefinition template
|
||||||
|
- id: T08
|
||||||
|
state_hub_task_id: cc0565bc-ccd4-4739-9c7e-088adaa9834b
|
||||||
|
status: done
|
||||||
|
title: Open activity-core handoff issue for resolver and definitions
|
||||||
|
- id: T09
|
||||||
|
state_hub_task_id: 23aeff77-3429-438b-a395-5c7b5dc2824e
|
||||||
|
status: done
|
||||||
|
title: Implement kaizen-agentic schedule prepare command
|
||||||
|
- id: T10
|
||||||
|
state_hub_task_id: cba9685b-408f-4292-8169-9965e7ad5c5b
|
||||||
|
status: done
|
||||||
|
title: Bundle memory brief metrics and agent prompt in prepare output
|
||||||
|
- id: T11
|
||||||
|
state_hub_task_id: 7c1b049b-5566-4b5e-9d82-d9c29fab0c84
|
||||||
|
status: done
|
||||||
|
title: Add schedule init scaffold for new repos
|
||||||
|
- id: T12
|
||||||
|
state_hub_task_id: 80a8e98a-9146-4a58-8fd3-8c37f7893960
|
||||||
|
status: done
|
||||||
|
title: Unit tests for schedule validate and prepare
|
||||||
|
- id: T13
|
||||||
|
state_hub_task_id: 9aee6348-d5ad-4413-b01c-5588b07c8ede
|
||||||
|
status: done
|
||||||
|
title: Pilot ActivityDefinition for weekly coach on custodian repos
|
||||||
|
- id: T14
|
||||||
|
state_hub_task_id: 697e612b-7531-4329-82fb-b0423ba86904
|
||||||
|
status: done
|
||||||
|
title: Pilot ActivityDefinition for weekly optimization review
|
||||||
|
- id: T15
|
||||||
|
state_hub_task_id: 92db5130-2212-4780-ab8f-1de3adf6c6f8
|
||||||
|
status: done
|
||||||
|
title: Smoke test end-to-end on two preselected repos
|
||||||
|
- id: T16
|
||||||
|
state_hub_task_id: bbecdcbf-42b8-4b48-9f85-e4c10e4ff3ab
|
||||||
|
status: done
|
||||||
|
title: Document operator enablement in PACKAGE_RELEASE and agency-framework
|
||||||
|
- id: T17
|
||||||
|
state_hub_task_id: a9e91d4b-990a-457a-9591-d1243db5b386
|
||||||
|
status: done
|
||||||
|
title: Emit kaizen.schedule.prepared event payload spec for activity-core
|
||||||
|
- id: T18
|
||||||
|
state_hub_task_id: 73986472-bf19-4b13-af1b-6505ab944459
|
||||||
|
status: done
|
||||||
|
title: Update wiki/EcosystemIntegration.md and CHANGELOG for v1.3.0---
|
||||||
|
|
||||||
|
# KAIZEN-WP-0006 — Scheduled Agent Execution via activity-core
|
||||||
|
|
||||||
|
**Status:** done
|
||||||
|
**Owner:** kaizen-agentic
|
||||||
|
**Repo:** kaizen-agentic
|
||||||
|
**Target version:** 1.3.0
|
||||||
|
**Depends on:** WP-0003 (metrics CLI), WP-0004 Part 2 (ActivityDefinition drafts)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Enable kaizen agents to run **on a regular schedule** against **preselected repos**,
|
||||||
|
orchestrated by **activity-core** and prepared by **kaizen-agentic** — without
|
||||||
|
this repo owning Temporal workers or LLM runtime.
|
||||||
|
|
||||||
|
Today, agents are markdown instruction sets invoked in coding-agent sessions.
|
||||||
|
WP-0004 committed three metrics-focused ActivityDefinitions (`enabled: false`) but
|
||||||
|
no general **agent dispatch** path, no **repo roster**, and no **per-repo schedule
|
||||||
|
manifest**. This workplan closes that gap.
|
||||||
|
|
||||||
|
### What “scheduled agent execution” means here
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
AC[activity-core cron] --> CR[context resolver]
|
||||||
|
CR --> ROSTER[preselected repos]
|
||||||
|
ROSTER --> RULE[ActivityDefinition rule]
|
||||||
|
RULE --> TASK[hub task per repo+agent]
|
||||||
|
TASK --> PREP[kaizen-agentic schedule prepare]
|
||||||
|
PREP --> SESSION[coding-agent session]
|
||||||
|
```
|
||||||
|
|
||||||
|
1. **activity-core** fires on cron (or event).
|
||||||
|
2. A **context resolver** returns repos from the preselected roster that have a
|
||||||
|
matching schedule entry and reachable `host_paths`.
|
||||||
|
3. For each `(repo, agent)` pair, activity-core creates a **task** (State Hub /
|
||||||
|
issue-core) with labels and a `schedule prepare` command.
|
||||||
|
4. A coding agent (human or automated session) runs `kaizen-agentic schedule prepare`
|
||||||
|
to assemble orientation: agent prompt, `memory brief`, metrics summary, repo
|
||||||
|
context — then executes the agent instructions in that repo.
|
||||||
|
|
||||||
|
Kaizen-agentic does **not** invoke Claude directly; it **prepares** and **validates**
|
||||||
|
the scheduled run contract.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
| Layer | WP-0004 state | WP-0006 adds |
|
||||||
|
|-------|---------------|--------------|
|
||||||
|
| activity-core | Three proposed definitions (metrics only) | Agent-run definitions + roster resolver |
|
||||||
|
| kaizen-agentic | CLI for metrics/memory | `schedule` command group + `.kaizen/schedule.yml` |
|
||||||
|
| state-hub | Registered repos + `host_paths` | Fleet roster opt-in + schedule metadata |
|
||||||
|
| Execution | Manual session | Cron → task → prepared session bundle |
|
||||||
|
|
||||||
|
**Prior WP-0006 placeholder** (agent wizard, template validation, doc generation)
|
||||||
|
moves to **KAIZEN-WP-0007**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 1 — Schedule Contract (repo-local)
|
||||||
|
|
||||||
|
Declare which agents run how often in each opted-in repo.
|
||||||
|
|
||||||
|
### `.kaizen/schedule.yml` (draft shape)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "1"
|
||||||
|
timezone: Europe/Berlin
|
||||||
|
agents:
|
||||||
|
coach:
|
||||||
|
cadence: weekly
|
||||||
|
cron: "0 9 * * 1" # optional override; default from ActivityDefinition
|
||||||
|
enabled: true
|
||||||
|
optimization:
|
||||||
|
cadence: weekly
|
||||||
|
cron: "0 10 * * 1"
|
||||||
|
enabled: true
|
||||||
|
tdd-workflow:
|
||||||
|
cadence: monthly
|
||||||
|
enabled: false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- [x] T01 — Write ADR-005: scheduled agent execution (roster, schedule file, dispatch, boundaries)
|
||||||
|
- [x] T02 — Add `docs/integrations/schedule-schema.md` + example under `docs/examples/.kaizen/schedule.yml`
|
||||||
|
- [x] T03 — Implement `kaizen-agentic schedule validate [--target PATH]` (schema + agent name checks)
|
||||||
|
|
||||||
|
### Definition of done
|
||||||
|
|
||||||
|
- ADR-005 accepted; referenced from `docs/agency-framework.md`
|
||||||
|
- Invalid schedule files fail validation with actionable errors
|
||||||
|
- Only agents installed in the project (or fleet defaults) may appear in schedule
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 2 — Fleet Roster (preselected repos)
|
||||||
|
|
||||||
|
Define which registered repos participate in fleet scheduling.
|
||||||
|
|
||||||
|
### Roster sources (combined)
|
||||||
|
|
||||||
|
| Source | Purpose |
|
||||||
|
|--------|---------|
|
||||||
|
| State Hub `GET /repos/` | Canonical slug list + `host_paths` |
|
||||||
|
| Repo opt-in | `.kaizen/schedule.yml` exists and `version` set |
|
||||||
|
| Optional hub flag | `kaizen_schedule_enabled: true` in repo metadata (design in T05) |
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- [x] T04 — Extend `docs/INTEGRATION_PATTERNS.md` Pattern 2 with roster + schedule model
|
||||||
|
- [x] T05 — Design doc: state-hub fields/filters for kaizen-scheduled repos (no state-hub code in this repo)
|
||||||
|
- [x] T06 — Spec `discover_kaizen_scheduled_repos` resolver for activity-core (inputs, output shape, errors)
|
||||||
|
|
||||||
|
### Definition of done
|
||||||
|
|
||||||
|
- Operators can list which repos are schedule-eligible without reading activity-core code
|
||||||
|
- Resolver spec is sufficient for activity-core implementers to add adapter (separate repo PR)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 3 — activity-core ActivityDefinitions (handoff)
|
||||||
|
|
||||||
|
Generalize WP-0004 metrics definitions into **agent-run** definitions.
|
||||||
|
|
||||||
|
### New reference definitions (kaizen-agentic commits drafts)
|
||||||
|
|
||||||
|
| Definition | Trigger | Agent | Prepare command |
|
||||||
|
|------------|---------|-------|-----------------|
|
||||||
|
| `weekly-coach-orientation` | Cron Mon 09:00 | `coach` | `schedule prepare coach` |
|
||||||
|
| `weekly-optimization-review` | Cron Mon 10:00 | `optimization` | `schedule prepare optimization` |
|
||||||
|
| `weekly-metrics-optimize` | *(existing)* | — | `metrics optimize` |
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- [x] T07 — Add `docs/integrations/activity-definitions/weekly-coach-orientation.md`
|
||||||
|
- [x] T08 — Open activity-core coordination issue/PR checklist (resolver + sync + `enabled: false` pilot)
|
||||||
|
- [x] T17 — Document `kaizen.schedule.prepared` event payload (for future event-driven runs)
|
||||||
|
|
||||||
|
### Definition of done
|
||||||
|
|
||||||
|
- Definition files use `for_each` over resolver output `context.scheduled_runs`
|
||||||
|
- Handoff checklist mirrors WP-0004 Pattern 2 activation steps
|
||||||
|
- No scheduling code added to kaizen-agentic
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 4 — Dispatch Prepare CLI
|
||||||
|
|
||||||
|
Bridge schedule intent to session-executable orientation.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
```
|
||||||
|
kaizen-agentic schedule validate [--target PATH]
|
||||||
|
kaizen-agentic schedule init [--target PATH] # scaffold .kaizen/schedule.yml
|
||||||
|
kaizen-agentic schedule prepare <agent> [--target PATH] [--format markdown|json]
|
||||||
|
kaizen-agentic schedule list [--target PATH] # show enabled entries from schedule.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
`schedule prepare` outputs a single bundle:
|
||||||
|
|
||||||
|
- Agent markdown (from installed or packaged `agents/agent-<name>.md`)
|
||||||
|
- `memory brief <agent>` output (if memory enabled)
|
||||||
|
- `metrics show <agent>` summary (if metrics exist)
|
||||||
|
- Repo SCOPE/TODO pointers when present
|
||||||
|
- Suggested session-close commands (`metrics record`, memory update)
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- [x] T09 — Implement `schedule` CLI command group skeleton
|
||||||
|
- [x] T10 — Wire `prepare` to memory + metrics + agent loader (reuse existing CLI internals)
|
||||||
|
- [x] T11 — Implement `schedule init` with sensible defaults (coach + optimization weekly, disabled tdd-workflow)
|
||||||
|
- [x] T12 — Tests for validate, init, prepare (temp repo fixtures)
|
||||||
|
|
||||||
|
### Definition of done
|
||||||
|
|
||||||
|
- `schedule prepare coach` prints non-empty orientation for a repo with agents installed
|
||||||
|
- Prepare works offline (no State Hub required) given local `.kaizen/` state
|
||||||
|
- CLI cheat sheet updated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 5 — Pilot and Enablement
|
||||||
|
|
||||||
|
Prove the loop on a small custodian repo set before fleet-wide enable.
|
||||||
|
|
||||||
|
### Pilot roster (initial)
|
||||||
|
|
||||||
|
- `kaizen-agentic` (dogfood)
|
||||||
|
- `the-custodian` (hub operator)
|
||||||
|
- One additional custodian-domain repo with `.kaizen/` state (TBD at pilot time)
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- [x] T13 — Commit pilot `weekly-coach-orientation` definition; map to custodian pilot repos
|
||||||
|
- [x] T14 — Commit pilot `weekly-optimization-review` definition
|
||||||
|
- [x] T15 — Smoke test: dry-run activity-core → manual `schedule prepare` on two repos → verify task payload
|
||||||
|
- [x] T16 — Operator docs: credentials, PATH, Gitea index, enabling `enabled: true` in activity-core
|
||||||
|
- [x] T18 — `wiki/EcosystemIntegration.md`, `CHANGELOG [Unreleased]`, `TODO.md` pointer
|
||||||
|
|
||||||
|
### Definition of done
|
||||||
|
|
||||||
|
- Two repos run through prepare successfully on a fixed schedule (manual or dry-run cron)
|
||||||
|
- activity-core owners have a clear PR path to enable one definition in staging
|
||||||
|
- Progress event recorded in State Hub at pilot completion
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sequencing
|
||||||
|
|
||||||
|
```
|
||||||
|
Part 1 (T01–T03) ──→ Part 4 (T09–T12) ──→ Part 5 (T13–T16)
|
||||||
|
│ │
|
||||||
|
Part 2 (T04–T06) ──→ Part 3 (T07–T08, T17)
|
||||||
|
```
|
||||||
|
|
||||||
|
Parts 1–2 can start in parallel. Part 4 depends on ADR + schema. Part 5 depends
|
||||||
|
on Part 3 drafts and Part 4 `prepare`. activity-core resolver implementation
|
||||||
|
runs in **activity-core** (parallel track after T06 spec).
|
||||||
|
|
||||||
|
Estimated effort: 4–6 sessions (kaizen-agentic) + 1–2 sessions (activity-core handoff).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Implementing Temporal workflows or context resolvers in activity-core (handoff only)
|
||||||
|
- Headless Claude Code / cloud agent runner integration (future; prepare output is runner-agnostic)
|
||||||
|
- Scheduling all 20 agents fleet-wide in v1.3.0 (pilot: coach + optimization)
|
||||||
|
- Replacing human review for high-risk agents (sys-medic, releaseManager)
|
||||||
|
- WP-0007 items: agent selection wizard, template schema enforcement, doc generation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
1. Operators declare a **preselected repo roster** and per-repo **schedules** without tribal knowledge.
|
||||||
|
2. activity-core can fire recurring tasks that reference `kaizen-agentic schedule prepare <agent>`.
|
||||||
|
3. A coding-agent session opened from a scheduled task has full orientation (memory + metrics + prompt).
|
||||||
|
4. Pilot runs on ≥2 custodian repos demonstrate the cron → task → prepare path.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## State Hub Task IDs
|
||||||
|
|
||||||
|
| Code | UUID |
|
||||||
|
|------|------|
|
||||||
|
| T01 | f513cf74-6e32-4dce-a3bc-2995f98a30de |
|
||||||
|
| T02 | dcb8d674-5472-4657-85bc-02ca9890fb92 |
|
||||||
|
| T03 | 1f9a4959-d200-4d7b-8666-96472e23d820 |
|
||||||
|
| T04 | 13ab4b76-ded2-4613-b8e2-15ab206d8c57 |
|
||||||
|
| T05 | 685b35f3-af20-483a-9136-150f5fff68dd |
|
||||||
|
| T06 | 5f0a0859-3ff5-4ec8-b916-35486f045b2b |
|
||||||
|
| T07 | 2fd0f00c-5cf1-47bd-9c12-aee65bca5932 |
|
||||||
|
| T08 | cc0565bc-ccd4-4739-9c7e-088adaa9834b |
|
||||||
|
| T09 | 23aeff77-3429-438b-a395-5c7b5dc2824e |
|
||||||
|
| T10 | cba9685b-408f-4292-8169-9965e7ad5c5b |
|
||||||
|
| T11 | 7c1b049b-5566-4b5e-9d82-d9c29fab0c84 |
|
||||||
|
| T12 | 80a8e98a-9146-4a58-8fd3-8c37f7893960 |
|
||||||
|
| T13 | 9aee6348-d5ad-4413-b01c-5588b07c8ede |
|
||||||
|
| T14 | 697e612b-7531-4329-82fb-b0423ba86904 |
|
||||||
|
| T15 | 92db5130-2212-4780-ab8f-1de3adf6c6f8 |
|
||||||
|
| T16 | bbecdcbf-42b8-4b48-9f85-e4c10e4ff3ab |
|
||||||
|
| T17 | a9e91d4b-990a-457a-9591-d1243db5b386 |
|
||||||
|
| T18 | 73986472-bf19-4b13-af1b-6505ab944459 |
|
||||||
|
|
||||||
|
**Hub workstream:** `kaizen-wp-0006-scheduled-agent-execution` (`d4edb92b-526f-4ee4-8efe-a9fa84ab6dff`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- WP-0004 `weekly-metrics-optimize` remains complementary; optimization agent schedule
|
||||||
|
may chain `schedule prepare optimization` then `metrics optimize` in task description
|
||||||
|
- `schedule prepare` output should be stable enough for activity-core task `description` templates
|
||||||
|
- Repo boundary: state-hub schema changes tracked in the-custodian; kaizen-agentic owns ADR + CLI + definition drafts
|
||||||
|
- Previous WP-0005 deferrals (wizard, validate schema, doc gen) → **KAIZEN-WP-0007**
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
---
|
||||||
|
id: KAIZEN-WP-0007
|
||||||
|
type: workplan
|
||||||
|
title: "Agent Authoring & Doc Generation (v1.4.0)"
|
||||||
|
domain: agents
|
||||||
|
repo: kaizen-agentic
|
||||||
|
status: done
|
||||||
|
owner: kaizen-agentic
|
||||||
|
topic_slug: custodian
|
||||||
|
state_hub_workstream_id: a8bc88a4-0ee3-44c6-aff5-9d7f54a316f5
|
||||||
|
depends_on:
|
||||||
|
- KAIZEN-WP-0006
|
||||||
|
created: "2026-06-18"
|
||||||
|
updated: "2026-06-18"
|
||||||
|
tasks:
|
||||||
|
- id: T01
|
||||||
|
state_hub_task_id: debaf0ac-47df-4bbd-aa1f-96c7b96a64ed
|
||||||
|
status: done
|
||||||
|
title: Fix idempotent CLAUDE.md doc regeneration (installer regex bug)
|
||||||
|
- id: T02
|
||||||
|
state_hub_task_id: e2d9bea8-243b-4b12-bba3-4d43a3bae71c
|
||||||
|
status: done
|
||||||
|
title: Reusable agent-docs module and kaizen-agentic docs generate command
|
||||||
|
- id: T03
|
||||||
|
state_hub_task_id: 57176887-5344-444c-aa5a-a4aea161ee71
|
||||||
|
status: done
|
||||||
|
title: Enforce agent frontmatter schema in validate
|
||||||
|
- id: T04
|
||||||
|
state_hub_task_id: 724e3862-602b-4ef0-88b8-ddb78a225046
|
||||||
|
status: done
|
||||||
|
title: kaizen-agentic create-agent scaffold for new agents
|
||||||
|
- id: T05
|
||||||
|
state_hub_task_id: 4d6e323c-9cad-49ce-9356-9192c20cc986
|
||||||
|
status: done
|
||||||
|
title: Unit tests for docs generate, schema validation, create-agent
|
||||||
|
- id: T06
|
||||||
|
state_hub_task_id: 6715aa6f-1ee0-4f22-9249-f1cd41763cd1
|
||||||
|
status: done
|
||||||
|
title: Docs, CLI cheat sheet, CHANGELOG for v1.4.0---
|
||||||
|
|
||||||
|
# KAIZEN-WP-0007 — Agent Authoring & Doc Generation
|
||||||
|
|
||||||
|
**Status:** done
|
||||||
|
**Owner:** kaizen-agentic
|
||||||
|
**Repo:** kaizen-agentic
|
||||||
|
**Target version:** 1.4.0
|
||||||
|
**Depends on:** WP-0006 (scheduled agent execution)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Close the WP-0005/WP-0006 deferrals — **agent selection/authoring**, **template
|
||||||
|
schema enforcement**, and **doc generation** — and fix the doc-generation defect
|
||||||
|
they exposed. After this workplan, authoring a new agent and keeping project
|
||||||
|
docs in sync is a first-class, idempotent, validated CLI flow.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
`AgentInstaller._update_documentation()` regenerates the `## Installed Agents`
|
||||||
|
block in a project's `CLAUDE.md`, but its regex
|
||||||
|
(`## Installed Agents.*?(?=##|\Z)`) is non-greedy and the `(?=##)` lookahead
|
||||||
|
matches `### Category` subheadings *inside* the section — so each run leaves the
|
||||||
|
old category lists and footer in place and appends a fresh copy. The block
|
||||||
|
duplicates and grows unbounded (reported to state-hub; the corrupting write is
|
||||||
|
ours). `validate` only checks dependencies and file existence — it does not
|
||||||
|
enforce the agent frontmatter schema — and there is no scaffolding command for
|
||||||
|
new agents.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
### T01 — Fix idempotent doc regeneration
|
||||||
|
- Anchor the replace to a top-level heading: `(?=\n## (?!#)|\Z)`.
|
||||||
|
- Add a regression test that runs regeneration twice and asserts exactly one
|
||||||
|
`## Installed Agents`, one footer, one block per category.
|
||||||
|
- Clean the existing baseline duplication in this repo's `CLAUDE.md`.
|
||||||
|
|
||||||
|
### T02 — Reusable agent-docs module + `docs generate`
|
||||||
|
- Extract the "Installed Agents" rendering into a pure function
|
||||||
|
(`render_installed_agents_section`) and an idempotent
|
||||||
|
`upsert_installed_agents_section(content, section)`.
|
||||||
|
- `_update_documentation` reuses them (no behavioral fork).
|
||||||
|
- New `kaizen-agentic docs generate [--target PATH] [--check]` refreshes the
|
||||||
|
section for installed agents; `--check` exits non-zero if it would change
|
||||||
|
(CI-friendly).
|
||||||
|
|
||||||
|
### T03 — Enforce agent frontmatter schema in `validate`
|
||||||
|
- Add `validate_frontmatter_schema()` to the registry: required `name`,
|
||||||
|
`description`, `category`; `category` ∈ `AgentCategory`; `memory` ∈
|
||||||
|
{`enabled`,`disabled`} when present; `model` a non-empty string when present.
|
||||||
|
- Surface schema errors in `kaizen-agentic validate` alongside dependency
|
||||||
|
errors, with actionable messages.
|
||||||
|
|
||||||
|
### T04 — `create-agent` scaffold
|
||||||
|
- `kaizen-agentic create-agent <name> [--category] [--description] [--memory]
|
||||||
|
[--target] [--force]` writes a schema-valid `agents/agent-<name>.md` with
|
||||||
|
frontmatter + section skeleton.
|
||||||
|
- Missing `--category`/`--description` fall back to interactive prompts.
|
||||||
|
- Refuses invalid category; refuses overwrite without `--force`; output passes
|
||||||
|
T03 validation.
|
||||||
|
|
||||||
|
### T05 — Tests
|
||||||
|
- `tests/test_agent_docs.py` — idempotency, `docs generate --check`.
|
||||||
|
- `tests/test_validate_schema.py` — schema pass/fail cases.
|
||||||
|
- `tests/test_create_agent.py` — scaffold + round-trips through registry.
|
||||||
|
|
||||||
|
### T06 — Docs
|
||||||
|
- `docs/CLI_CHEAT_SHEET.md` — `docs generate`, `create-agent`, schema validate.
|
||||||
|
- `docs/agency-framework.md` — authoring + doc-sync note.
|
||||||
|
- `CHANGELOG.md` `[Unreleased]`; `TODO.md` pointer.
|
||||||
|
|
||||||
|
## Definition of Done
|
||||||
|
|
||||||
|
- Doc regeneration is idempotent (N runs == 1 run); baseline cleaned.
|
||||||
|
- `kaizen-agentic validate` fails on malformed agent frontmatter with clear errors.
|
||||||
|
- `kaizen-agentic create-agent` produces an agent that passes `validate`.
|
||||||
|
- `kaizen-agentic docs generate --check` is green on a synced repo.
|
||||||
|
- Full test suite + `make release-check` green.
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Multi-file agent packages / protocol scaffolding (separate workplan).
|
||||||
|
- Publishing generated docs to external knowledge bases (info-tech-canon).
|
||||||
|
- Changing the agent frontmatter schema itself (only enforcing the current one).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Boundary: the doc-regeneration *trigger* (a state-hub routine invoking the
|
||||||
|
installer mid-session) is tracked separately in the state-hub inbox; this
|
||||||
|
workplan fixes the *write* so the trigger becomes harmless/idempotent.
|
||||||
|
- `create-agent` writes to `agents/`; remember `make agents-sync-package` before
|
||||||
|
release so packaged `data/agents/` parity holds (existing release-check gate).
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
---
|
||||||
|
id: KAIZEN-WP-0008
|
||||||
|
type: workplan
|
||||||
|
title: "Coulomb-loop supplier engagement (customer-repo playbook)"
|
||||||
|
domain: agents
|
||||||
|
repo: kaizen-agentic
|
||||||
|
status: done
|
||||||
|
owner: kaizen-agentic
|
||||||
|
topic_slug: custodian
|
||||||
|
customer_repo: coulomb-loop
|
||||||
|
created: "2026-06-18"
|
||||||
|
updated: "2026-06-18"
|
||||||
|
depends_on:
|
||||||
|
- KAIZEN-WP-0006
|
||||||
|
- KAIZEN-WP-0004
|
||||||
|
tasks:
|
||||||
|
- id: T01
|
||||||
|
status: done
|
||||||
|
title: Document customer engagement repo layout from coulomb-loop reference
|
||||||
|
- id: T02
|
||||||
|
status: done
|
||||||
|
title: Add docs/integrations/customer-engagement-playbook.md skeleton
|
||||||
|
- id: T03
|
||||||
|
status: done
|
||||||
|
title: Implement metrics record --emit-event for kaizen.metrics.recorded
|
||||||
|
- id: T04
|
||||||
|
status: done
|
||||||
|
title: Add schedule init --engagement mode for customer repos
|
||||||
|
- id: T05
|
||||||
|
status: done
|
||||||
|
title: Support pilot schedule init on kaizen-agentic the-custodian activity-core
|
||||||
|
- id: T06
|
||||||
|
status: done
|
||||||
|
title: Draft ADR-006 customer engagement convention
|
||||||
|
- id: T07
|
||||||
|
status: done
|
||||||
|
title: Absorb coulomb-loop supplier-notes into playbook v1
|
||||||
|
- id: T08
|
||||||
|
status: done
|
||||||
|
title: ActivityDefinition override manifest design for hybrid sync
|
||||||
|
- id: T09
|
||||||
|
status: done
|
||||||
|
title: Tests for emit-event and engagement init
|
||||||
|
- id: T10
|
||||||
|
status: done
|
||||||
|
title: Update CHANGELOG wiki and cross-link coulomb-loop INTENT
|
||||||
|
state_hub_workstream_id: "80f473eb-d052-4f50-a633-806f03c469be"
|
||||||
|
---
|
||||||
|
|
||||||
|
# KAIZEN-WP-0008 — Coulomb-loop Supplier Engagement
|
||||||
|
|
||||||
|
**Status:** done
|
||||||
|
**Owner:** kaizen-agentic (supplier)
|
||||||
|
**Customer:** `coulomb-loop` (coulomb_social domain)
|
||||||
|
**Depends on:** WP-0006 (schedule contract), WP-0004 (activity-core integration)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Deliver supplier capabilities for Coulomb's self-improvement loop engagement and
|
||||||
|
**generalize learnings** into a reusable customer-repo bootstrap playbook — so the
|
||||||
|
next engagement requires ≤50% setup effort compared to coulomb-loop.
|
||||||
|
|
||||||
|
This workplan is the **supplier mirror** of coulomb-loop LOOP-WP-0001–0004.
|
||||||
|
Customer-specific operations stay in `coulomb-loop`; reusable IP stays here.
|
||||||
|
|
||||||
|
## Engagement model
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
CL[coulomb-loop customer]
|
||||||
|
KA[kaizen-agentic supplier]
|
||||||
|
TR[target repos fleet]
|
||||||
|
AC[activity-core]
|
||||||
|
|
||||||
|
CL -->|contracts rosters definitions| AC
|
||||||
|
KA -->|agents CLI ADRs playbook| CL
|
||||||
|
KA -->|schedule prepare metrics| TR
|
||||||
|
AC -->|tasks| TR
|
||||||
|
```
|
||||||
|
|
||||||
|
See coulomb-loop `docs/adr/ADR-002-customer-supplier-boundary.md`.
|
||||||
|
|
||||||
|
## Sequencing (per DEC-004 default — smoke-first)
|
||||||
|
|
||||||
|
```
|
||||||
|
Part 1 (T01–T02, T05) ── parallel with coulomb-loop smoke test
|
||||||
|
Part 2 (T03–T04, T06–T09) ── after first hourly E2E pass
|
||||||
|
Part 3 (T07–T08, T10) ── after LOOP-WP-0004 supplier-notes available
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 1 — Document and support smoke test
|
||||||
|
|
||||||
|
## Document customer engagement repo layout
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T01
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "177bb16c-6239-43f2-8d99-f4498c31d74a"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `docs/integrations/customer-engagement-repo-layout.md` from coulomb-loop
|
||||||
|
reference:
|
||||||
|
|
||||||
|
```
|
||||||
|
customer-repo/
|
||||||
|
INTENT.md SCOPE.md
|
||||||
|
workplans/LOOP-WP-* or <PREFIX>-WP-*
|
||||||
|
docs/adr/ docs/decisions/
|
||||||
|
history/
|
||||||
|
activity-definitions/ # customer-owned copies
|
||||||
|
loops/<loop-id>/ # roster cadence health
|
||||||
|
```
|
||||||
|
|
||||||
|
No code — layout contract only.
|
||||||
|
|
||||||
|
## Playbook skeleton
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T02
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "90bd0fc2-6e49-4a59-9a78-91e749cef8a6"
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `docs/integrations/customer-engagement-playbook.md`:
|
||||||
|
|
||||||
|
1. Register repo (state-hub `register_project.sh`)
|
||||||
|
2. Write INTENT + 4 loop workplans
|
||||||
|
3. Run `fix-consistency`
|
||||||
|
4. Pilot `schedule init` on target repos
|
||||||
|
5. Sync ActivityDefinitions to activity-core
|
||||||
|
6. Bootstrap hourly → regulator promotes cadence
|
||||||
|
|
||||||
|
Link to coulomb-loop as reference implementation.
|
||||||
|
|
||||||
|
## Support pilot schedule init
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T05
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "a48598b7-2a33-46ef-8594-6a2702459f39"
|
||||||
|
```
|
||||||
|
|
||||||
|
Completed 2026-06-18 on kaizen-agentic, the-custodian, activity-core. Bootstrap
|
||||||
|
hourly crons patched manually; friction logged in coulomb-loop `supplier-notes.md`.
|
||||||
|
|
||||||
|
Execute on pilot repos (after DEC-001 approval):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for repo in kaizen-agentic the-custodian activity-core; do
|
||||||
|
cd ~/$repo
|
||||||
|
kaizen-agentic schedule init --timezone Europe/Berlin
|
||||||
|
kaizen-agentic memory init coach
|
||||||
|
kaizen-agentic memory init optimization
|
||||||
|
kaizen-agentic schedule validate
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Record friction in coulomb-loop `loops/kaizen-stack/supplier-notes.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 2 — Supplier automation
|
||||||
|
|
||||||
|
## metrics record --emit-event
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T03
|
||||||
|
status: done
|
||||||
|
priority: medium
|
||||||
|
state_hub_task_id: "26ee0f8d-2b69-4796-b276-b76238d67546"
|
||||||
|
```
|
||||||
|
|
||||||
|
Emit NATS event `kaizen.metrics.recorded` when flag set:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic metrics record coach --success --time 120 --quality 0.9 --emit-event
|
||||||
|
```
|
||||||
|
|
||||||
|
Payload per coulomb-loop LOOP-WP-0002 T03 / `low-success-rate-review` definition.
|
||||||
|
Default: off (backward compatible).
|
||||||
|
|
||||||
|
## schedule init --engagement
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T04
|
||||||
|
status: done
|
||||||
|
priority: medium
|
||||||
|
state_hub_task_id: "62324bd2-1737-4864-889c-56179d0d11e8"
|
||||||
|
```
|
||||||
|
|
||||||
|
Scaffold customer-target schedule with bootstrap crons:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kaizen-agentic schedule init --engagement coulomb-loop \
|
||||||
|
--agents coach,optimization --bootstrap-cadence hourly
|
||||||
|
```
|
||||||
|
|
||||||
|
Writes hourly crons per ADR-003; documents engagement slug in schedule comment.
|
||||||
|
|
||||||
|
## ADR-006 customer engagement convention
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T06
|
||||||
|
status: done
|
||||||
|
priority: medium
|
||||||
|
state_hub_task_id: "5c06cdd9-655d-4837-b725-1f89b83db6d4"
|
||||||
|
```
|
||||||
|
|
||||||
|
`docs/adr/ADR-006-customer-engagement-convention.md` — formalize supplier/customer
|
||||||
|
split, `.kaizen/` placement in target repos, playbook lifecycle.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T09
|
||||||
|
status: done
|
||||||
|
priority: medium
|
||||||
|
state_hub_task_id: "f45077ea-5d24-4a85-bac2-ab9a3f61c20b"
|
||||||
|
```
|
||||||
|
|
||||||
|
Covered by `tests/test_metrics_emit_event.py` and `tests/test_schedule_cli.py`
|
||||||
|
(engagement init + module presets).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 3 — Playbook v1 and hybrid sync design
|
||||||
|
|
||||||
|
## Absorb supplier-notes into playbook v1
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T07
|
||||||
|
status: done
|
||||||
|
priority: low
|
||||||
|
state_hub_task_id: "0ef49fb5-af2f-4adf-aa90-1ea2cf389d00"
|
||||||
|
```
|
||||||
|
|
||||||
|
After LOOP-WP-0004 T07 draft in coulomb-loop, merge into playbook v1.
|
||||||
|
Target: second customer can copy template repo and run checklist in one session.
|
||||||
|
|
||||||
|
## ActivityDefinition override manifest
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T08
|
||||||
|
status: done
|
||||||
|
priority: low
|
||||||
|
state_hub_task_id: "c9bee570-89b5-43e5-aabc-23c7dcc4e30c"
|
||||||
|
```
|
||||||
|
|
||||||
|
Design-only (implements DEC-003 option C): YAML manifest mapping supplier
|
||||||
|
definition id → customer cron/labels/enabled overrides. No runtime in v1.
|
||||||
|
|
||||||
|
## Documentation release
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: KAIZEN-WP-0008-T10
|
||||||
|
status: done
|
||||||
|
priority: low
|
||||||
|
state_hub_task_id: "052a592b-ae7c-4213-9e09-eb8b37119d5e"
|
||||||
|
```
|
||||||
|
|
||||||
|
Update `wiki/EcosystemIntegration.md`, `CHANGELOG [Unreleased]`, cross-link
|
||||||
|
coulomb-loop INTENT from `docs/integrations/customer-engagement-playbook.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-004 follow-on (customer accepted 2026-06-18)
|
||||||
|
|
||||||
|
After bootstrap metrics baseline, supplier may add:
|
||||||
|
|
||||||
|
```
|
||||||
|
kaizen-agentic metrics rotation-signals [--target PATH]
|
||||||
|
```
|
||||||
|
|
||||||
|
Reads `.kaizen/metrics/` + optimizer output; emits saturation score per
|
||||||
|
`coulomb-loop/loops/regulator/rotation-policy.yml`. Feeds LOOP-WP-0004 T09.
|
||||||
|
Track as KAIZEN-WP-0008 extension task if needed after T03 ships.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- activity-core resolver implementation (activity-core repo)
|
||||||
|
- coulomb-loop workplan execution (customer repo)
|
||||||
|
- Fleet-wide rollout beyond agreed pilot (DEC-001)
|
||||||
|
|
||||||
|
## Success criteria
|
||||||
|
|
||||||
|
1. Pilot repos have valid `.kaizen/schedule.yml` via supplier CLI
|
||||||
|
2. `metrics record --emit-event` enables LOOP-WP-0002 event path
|
||||||
|
3. Playbook v1 committed; coulomb-loop cited as reference
|
||||||
|
4. ADR-006 accepted
|
||||||
|
|
||||||
|
## Customer workplans (do not duplicate here)
|
||||||
|
|
||||||
|
| Customer WP | Supplier support |
|
||||||
|
|-------------|------------------|
|
||||||
|
| LOOP-WP-0001 | T05 schedule init; activity-core handoff docs |
|
||||||
|
| LOOP-WP-0002 | T03 emit-event |
|
||||||
|
| LOOP-WP-0003 | scope-analyst agent (existing) |
|
||||||
|
| LOOP-WP-0004 | T07 playbook feedback |
|
||||||
Reference in New Issue
Block a user