feat: schedule init --engagement for customer bootstrap presets
Some checks failed
ci / test (push) Has been cancelled
Some checks failed
ci / test (push) Has been cancelled
Add --engagement, --agents, and --bootstrap-cadence flags to scaffold hourly/daily/weekly engagement schedules. Hourly bootstrap keeps cadence: daily with hourly cron overrides per coulomb-loop ADR-003. Document activity-core requirements in activity-core-handoff-engagement.md. Closes KAIZEN-WP-0008 T02 and T04.
This commit is contained in:
@@ -11,7 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- **`metrics record --emit-event`** — publishes `kaizen.metrics.recorded` NATS
|
- **`metrics record --emit-event`** — publishes `kaizen.metrics.recorded` NATS
|
||||||
envelope for activity-core event-driven definitions (optional `nats-py` via
|
envelope for activity-core event-driven definitions (optional `nats-py` via
|
||||||
`pip install 'kaizen-agentic[events]'`)
|
`pip install 'kaizen-agentic[events]'`)
|
||||||
|
- **`schedule init --engagement`** — bootstrap presets for customer engagements
|
||||||
|
(`--bootstrap-cadence hourly|daily|weekly`)
|
||||||
- **Event contract** — `docs/integrations/kaizen-metrics-recorded-event.md`
|
- **Event contract** — `docs/integrations/kaizen-metrics-recorded-event.md`
|
||||||
|
- **Engagement handoff** — `docs/integrations/activity-core-handoff-engagement.md`
|
||||||
|
|
||||||
## [1.4.0] - 2026-06-18
|
## [1.4.0] - 2026-06-18
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ Session-close template: `docs/templates/session-close-protocol.md`
|
|||||||
# Opt this repo into fleet scheduling
|
# Opt this repo into fleet scheduling
|
||||||
kaizen-agentic schedule init # coach + optimization weekly
|
kaizen-agentic schedule init # coach + optimization weekly
|
||||||
kaizen-agentic schedule init --timezone UTC # override timezone
|
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 validate # schema + agent-name checks
|
||||||
kaizen-agentic schedule list # enabled entries (--all incl. disabled)
|
kaizen-agentic schedule list # enabled entries (--all incl. disabled)
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ embed it in a task `description` or a runner can parse it.
|
|||||||
|
|
||||||
```
|
```
|
||||||
kaizen-agentic schedule init [--target PATH] [--timezone TZ] [--force]
|
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 validate [--target PATH]
|
||||||
kaizen-agentic schedule list [--target PATH] [--all]
|
kaizen-agentic schedule list [--target PATH] [--all]
|
||||||
kaizen-agentic schedule prepare <agent> [--target PATH] [--format markdown|json]
|
kaizen-agentic schedule prepare <agent> [--target PATH] [--format markdown|json]
|
||||||
|
|||||||
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)
|
||||||
66
docs/integrations/customer-engagement-playbook.md
Normal file
66
docs/integrations/customer-engagement-playbook.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Customer Engagement Playbook (supplier)
|
||||||
|
|
||||||
|
How kaizen-agentic supports a **customer engagement repo** (e.g. coulomb-loop)
|
||||||
|
that orchestrates improvement loops across a pilot roster.
|
||||||
|
|
||||||
|
## Roles
|
||||||
|
|
||||||
|
| Repo | Role |
|
||||||
|
|------|------|
|
||||||
|
| Customer (coulomb-loop) | Roster, ActivityDefinition copies, cadence policy, loop health |
|
||||||
|
| Supplier (kaizen-agentic) | Agents, CLI, integration contracts |
|
||||||
|
| Target repos | `.kaizen/` state (schedule, memory, metrics) |
|
||||||
|
| activity-core | Cron + event orchestration, task creation |
|
||||||
|
|
||||||
|
## Bootstrap checklist
|
||||||
|
|
||||||
|
### 1. Customer repo
|
||||||
|
|
||||||
|
- Register engagement in state-hub
|
||||||
|
- Commit pilot roster (`loops/kaizen-stack/roster.yaml`)
|
||||||
|
- Copy ActivityDefinitions to `activity-definitions/`
|
||||||
|
- 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 writes `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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. activity-core
|
||||||
|
|
||||||
|
Hand off [activity-core-handoff-engagement.md](activity-core-handoff-engagement.md)
|
||||||
|
requirements before enabling event-driven quality escalation.
|
||||||
|
|
||||||
|
## Cadence promotion
|
||||||
|
|
||||||
|
Customer regulator (LOOP-WP-0004) approves promotion. Re-init or patch schedules:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stabilize phase
|
||||||
|
kaizen-agentic schedule init --engagement <slug> \
|
||||||
|
--bootstrap-cadence daily --force
|
||||||
|
|
||||||
|
# Operate phase
|
||||||
|
kaizen-agentic schedule init --engagement <slug> \
|
||||||
|
--bootstrap-cadence weekly --force
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [KAIZEN-WP-0008](../../workplans/kaizen-agentic-WP-0008-coulomb-loop-supplier-engagement.md)
|
||||||
|
- coulomb-loop `INTENT.md` and `workplans/LOOP-WP-*`
|
||||||
@@ -70,11 +70,18 @@ the distribution) may appear in a schedule.
|
|||||||
kaizen-agentic schedule init # defaults: coach + optimization weekly
|
kaizen-agentic schedule init # defaults: coach + optimization weekly
|
||||||
kaizen-agentic schedule init --timezone UTC # override timezone
|
kaizen-agentic schedule init --timezone UTC # override timezone
|
||||||
kaizen-agentic schedule init --force # overwrite existing
|
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
|
The default scaffold enables `coach` and `optimization` weekly and declares
|
||||||
`tdd-workflow` monthly but **disabled** (operator opts in deliberately).
|
`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
|
## Listing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from .optimization import OptimizationLoop, MIN_SAMPLES_FOR_RECOMMENDATIONS
|
|||||||
from .schedule import (
|
from .schedule import (
|
||||||
ScheduleError,
|
ScheduleError,
|
||||||
default_schedule_yaml,
|
default_schedule_yaml,
|
||||||
|
engagement_schedule_yaml,
|
||||||
load_schedule,
|
load_schedule,
|
||||||
schedule_path,
|
schedule_path,
|
||||||
validate_schedule,
|
validate_schedule,
|
||||||
@@ -1455,8 +1456,39 @@ def schedule_validate(target: str):
|
|||||||
"--timezone", default="Europe/Berlin", show_default=True, help="Schedule timezone"
|
"--timezone", default="Europe/Berlin", show_default=True, help="Schedule timezone"
|
||||||
)
|
)
|
||||||
@click.option("--force", is_flag=True, help="Overwrite an existing schedule.yml")
|
@click.option("--force", is_flag=True, help="Overwrite an existing schedule.yml")
|
||||||
def schedule_init(target: str, timezone: str, force: bool):
|
@click.option(
|
||||||
"""Scaffold a default .kaizen/schedule.yml (coach + optimization weekly)."""
|
"--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))
|
path = schedule_path(_project_root(target))
|
||||||
|
|
||||||
if path.exists() and not force:
|
if path.exists() and not force:
|
||||||
@@ -1464,9 +1496,41 @@ def schedule_init(target: str, timezone: str, force: bool):
|
|||||||
click.echo(" Use --force to overwrite.")
|
click.echo(" Use --force to overwrite.")
|
||||||
return
|
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.parent.mkdir(parents=True, exist_ok=True)
|
||||||
path.write_text(default_schedule_yaml(timezone=timezone), encoding="utf-8")
|
path.write_text(yaml_text, encoding="utf-8")
|
||||||
click.echo(f"Initialized schedule: {path}")
|
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")
|
click.echo(" Validate with: kaizen-agentic schedule validate")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,29 @@ DEFAULT_AGENTS: Dict[str, Dict[str, Any]] = {
|
|||||||
"tdd-workflow": {"cadence": "monthly", "enabled": False},
|
"tdd-workflow": {"cadence": "monthly", "enabled": False},
|
||||||
}
|
}
|
||||||
DEFAULT_TIMEZONE = "Europe/Berlin"
|
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):
|
class ScheduleError(Exception):
|
||||||
@@ -183,3 +206,56 @@ def default_schedule_yaml(timezone: str = DEFAULT_TIMEZONE) -> str:
|
|||||||
)
|
)
|
||||||
body = yaml.safe_dump(document, sort_keys=False, default_flow_style=False)
|
body = yaml.safe_dump(document, sort_keys=False, default_flow_style=False)
|
||||||
return header + body
|
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
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import json
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import yaml
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
|
||||||
from kaizen_agentic.cli import cli
|
from kaizen_agentic.cli import cli
|
||||||
from kaizen_agentic.schedule import (
|
from kaizen_agentic.schedule import (
|
||||||
ScheduleError,
|
ScheduleError,
|
||||||
|
engagement_schedule_yaml,
|
||||||
parse_schedule,
|
parse_schedule,
|
||||||
schedule_path,
|
schedule_path,
|
||||||
validate_schedule,
|
validate_schedule,
|
||||||
@@ -30,6 +32,20 @@ def project_dir(tmp_path: Path) -> Path:
|
|||||||
|
|
||||||
|
|
||||||
class TestScheduleModule:
|
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):
|
def test_parse_requires_version(self):
|
||||||
with pytest.raises(ScheduleError):
|
with pytest.raises(ScheduleError):
|
||||||
parse_schedule({"agents": {}})
|
parse_schedule({"agents": {}})
|
||||||
@@ -67,6 +83,67 @@ class TestScheduleCli:
|
|||||||
assert path.exists()
|
assert path.exists()
|
||||||
assert "coach" in path.read_text()
|
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(
|
def test_init_no_overwrite_without_force(
|
||||||
self, runner: CliRunner, project_dir: Path
|
self, runner: CliRunner, project_dir: Path
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ tasks:
|
|||||||
status: todo
|
status: todo
|
||||||
title: Document customer engagement repo layout from coulomb-loop reference
|
title: Document customer engagement repo layout from coulomb-loop reference
|
||||||
- id: T02
|
- id: T02
|
||||||
status: todo
|
status: done
|
||||||
title: Add docs/integrations/customer-engagement-playbook.md skeleton
|
title: Add docs/integrations/customer-engagement-playbook.md skeleton
|
||||||
- id: T03
|
- id: T03
|
||||||
status: done
|
status: done
|
||||||
title: Implement metrics record --emit-event for kaizen.metrics.recorded
|
title: Implement metrics record --emit-event for kaizen.metrics.recorded
|
||||||
- id: T04
|
- id: T04
|
||||||
status: todo
|
status: done
|
||||||
title: Add schedule init --engagement mode for customer repos
|
title: Add schedule init --engagement mode for customer repos
|
||||||
- id: T05
|
- id: T05
|
||||||
status: done
|
status: done
|
||||||
@@ -188,7 +188,7 @@ Default: off (backward compatible).
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: KAIZEN-WP-0008-T04
|
id: KAIZEN-WP-0008-T04
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "62324bd2-1737-4864-889c-56179d0d11e8"
|
state_hub_task_id: "62324bd2-1737-4864-889c-56179d0d11e8"
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user