Install
$ agentstack add skill-mataeil-ooda-loop-ooda-config ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
ooda-config: Configuration Management CLI
View, modify, and validate config.json without editing JSON by hand. All writes: back up to config.json.bak, then re-parse to confirm valid JSON.
Step 0: HALT + Preflight
Read config.safety.halt_file (default agent/safety/HALT). If that path exists: HALT: . ooda-config aborted. — stop.
If config.json is missing and subcommand is not validate: config.json not found. Run /ooda-setup to create one. — stop.
Parse the user's invocation to one of: show | level | domain add | domain remove | domain list | safety | validate | lens review | lens reset | action {list|approve|defer|reject|prioritize} | mode/season | rotation {show|reset} | context show | rollback {cycle} | auto-merge {on|off}. Unknown subcommand → print the Usage block and exit.
Step A — show
Print a compact summary: schemaversion, project fields, domain list with weight/skill/enabled status, current progressivecomplexity level and name, and key safety values (haltfile presence, confidencethreshold, maxprsper_cycle).
Step B — level {N}
- Validate N is 0–3; fail fast otherwise.
- If N == 3, print explicit warning and require a typed confirmation phrase:
``` [DANGER] Level 3 enables AUTONOMOUS mode:
- The agent autonomously writes code and opens PRs (no prompt).
- By DEFAULT those are DRAFT PRs you review and merge — nothing reaches
main without your click. (You stay in command.)
- Auto-merge is a SEPARATE opt-in: only if you ALSO run
/ooda-config auto-merge on (safety.enableautomerge=true) will it merge LOW-RISK PRs by itself — non-protected paths, → ("")`.
Step C — domain add {name}
Ask the user in sequence for: weight (float, default 1.0), primaryskill (must start with "/"), statefile path (default agent/state/.json).
Back up config.json. Insert into config.domains:
{ "weight": , "state_file": "", "primary_skill": "",
"chain": [], "branch_prefix": "auto//", "fallback": true,
"enabled": true, "status": "active" }
(status is the field evolve's filters treat as authoritative — a domain without it would never be selected. enabled is kept for back-compat; evolve treats a domain with enabled: true and NO status field as "active".) Create empty state file if absent ({ "schema_version": "1.0.0", "domain": "", "last_run": null }). Append primary_skill to safety.skill_allowlist if not already present. Write + validate. Print confirmation showing state file status and allowlist change.
Step D — domain remove {name}
Look up config.domains.. Not found or already disabled → print message and exit. Back up. Set enabled: false (soft-delete — data retained). Write + validate. Print: Domain disabled: (re-enable by setting enabled: true)
Step E — domain list
Print a table: name, status (enabled/disabled), weight, primaryskill, statefile. Footer: Total: domains, enabled. If config.domains is empty: No domains configured.
Step F — safety
Print the full safety section as a labeled key-value block: haltfile (with presence indicator), confidencethreshold, mincycleintervalminutes, maxprspercycle, maxfilesperpr, maxlinesperpr, firstcycleobserveonly, protectedpaths list, and skill_allowlist.
Step G — validate
The full check list (1–27) is defined ONCE in "Step G — validate (extended)" below — that section is canonical. (An earlier 17-check copy of this list lived here and drifted from the extended one; never maintain two lists.)
Lens Management
Step H — lens review {domain}
Show the lens snapshot for the named domain.
- Look up the domain in
config.domains. Not found →Domain not found:and exit. - Derive the lens path:
agent/state//lens.json(orlens_filefield if present in config). - If lens.json does not exist →
No lens data for . Run a cycle first.and exit. - Print the full contents of lens.json as a labeled key-value block, including all evidence trail entries (timestamps, sources, confidence deltas).
Step I — lens reset {domain}
Wipe the lens snapshot for the named domain, forcing a fresh start on the next cycle.
- Look up the domain in
config.domains. Not found →Domain not found:and exit. - Derive the lens path the same way as Step H.
- If lens.json does not exist →
No lens file found for . Nothing to reset.and exit. - Ask for confirmation:
Reset lens for ? This clears all accumulated evidence. (yes/no)
Any answer other than "yes" → Cancelled. and exit.
- Back up the existing lens file to
lens.json.bakin the same directory before overwriting.
Print: Backed up: .bak
- Overwrite lens.json with the FULL empty schema (every array evolve 5-E reads
must exist — a reset file missing focus_items/learned_thresholds/ discovered_signals breaks the next lens update): { "schema_version": "1.0.0", "domain": "", "reset_at": "", "focus_items": [], "learned_thresholds": [], "discovered_signals": [], "deprecated_items": [], "evidence": [] }
- Print:
Lens reset: (evidence cleared, backup saved, next cycle starts fresh)
Action Queue Management
Step J — action list
Print pending actions from agent/state/evolve/action_queue.json:
[Action Queue] {pending_count} pending, {completed_count} completed
| # | ID | Title | RICE | Age | Status |
|---|--------|------------------------------|-------|------|---------|
| 1 | a15-1 | Update context.json names | 50400 | 3.2d | pending |
| 2 | a17-1 | Pipeline FTS index | 25200 | 3.0d | approved |
Show effective_rice (with decay applied) and age since extracted_at.
Step K — action approve {id}
Set action status to "approved". Approved items are prioritized over pending items in dev-cycle selection regardless of RICE score.
1. Find action by id in action_queue.pending
2. If not found: "Action not found: {id}" — exit
3. Set status = "approved", approved_at = now
4. Boost effective_rice by 20% (item.effective_rice *= 1.2)
5. Print "Approved: {title} (effective RICE: {new_rice})"
Step L — action defer {id} [days]
Defer an action for N days (default 7). Deferred items are excluded from dev-cycle selection until their defer period expires.
1. Find action by id
2. Set status = "deferred", deferred_until = now + N days
3. Print "Deferred: {title} until {date} ({N} days)"
Step M — action reject {id} [reason]
Move action to completed with status "rejected_by_human".
1. Find action by id
2. Move to completed array with status = "rejected_by_human", reason = reason
3. Print "Rejected: {title} — {reason or 'no reason given'}"
Step N — action prioritize {id}
Move action to top of queue by setting effective_rice above current highest.
1. Find action by id
2. Set effective_rice = max(all pending effective_rice) + 1.0
3. Re-sort pending by effective_rice descending
4. Print "Prioritized: {title} → top of queue (RICE: {new_rice})"
Step O — mode {name} (alias: season {name})
Switch season/phase mode. Mode overrides domain weights, disables selected domains for the season, and may bump signal bonuses. Changes take effect on the NEXT cycle (they are read in-memory at Step 1-A; disk config.json is updated so /ooda-status shows the current mode).
1. Read config.season_modes.modes
2. If modes not configured: "No season modes defined. Add season_modes to config.json." — exit
3. If {name} not in modes: "Unknown mode: {name}. Available: {list}" — exit
4. Back up config.json
5. Set config.season_modes.current_mode = {name}
6. Set config.season_modes.enabled = true (in case it was false)
7. Print applied overrides:
- weight_overrides: {domain: multiplier}
- disabled_domains: [...]
- signal_bonuses: {signal: value}
8. Write + validate JSON
9. Print "Season mode changed: {old} → {name}"
Step O1 — season list
List all defined modes with a one-line summary.
1. Read config.season_modes.modes
2. For each mode: print "- {name}: {len(weight_overrides)} weights, {len(disabled_domains)} disabled, {len(signal_bonuses)} signal bonuses"
3. Print: "Current: {current_mode}" (or "(season_modes disabled)" if enabled=false)
Step O2 — season show
Print the full active-mode overrides applied to the current scoring table.
1. Read config.season_modes
2. If not enabled: print "Season modes disabled." — exit
3. For the current_mode:
- Print "Mode: {current_mode}"
- Print weight_overrides with the previous weights for comparison:
" service_health: config=2.0 -> mode=1.0 (effective)"
- Print disabled_domains: " disabled: [competitors]"
- Print signal_bonuses: " signal bonuses: {health_alert_bonus: +5.0}"
Step P — rotation show {domain}
Show the rotation list and current cursor for a domain.
1. Look up domain in config.domains
2. If no `rotation` field: "Domain '{name}' has no rotation list." — exit
3. Read cursor from agent/state/{domain}/rotation_cursor.json (missing = cursor 0)
4. Print the rotation list with an arrow at the current cursor position:
[thumbnail-editor, dashboard -> stats, billing]
5. Print: "Next focus_item: {list[cursor]}"
Step P1 — rotation reset {domain}
Reset the rotation cursor to 0 for a domain.
1. Look up domain in config.domains
2. If no `rotation` field: "Domain '{name}' has no rotation list." — exit
3. Back up agent/state/{domain}/rotation_cursor.json to .bak (if present)
4. Write {"cursor": 0, "last_updated": now, "focus_item": rotation_list[0]}
5. Print: "Rotation cursor reset: {domain} -> 0 ({first_focus_item})"
Step Q — context show
Print the current active_context config + the loaded blob contents.
1. Read config.active_context
2. If no active_context.path: "No active_context configured." — exit
3. Try to read the file at that path:
- If missing: "active_context file missing: {path}"
- If malformed JSON: "active_context file is not valid JSON"
- If OK: print path, mtime age, refresh_skill (if any), refresh_interval_hours,
and the blob contents (pretty-printed, cap at 80 lines)
Step R — rollback {cycle}
Revert the repo + state to a recorded checkpoint. Checkpoints are written by evolve 4-C2 when safety.enable_rollback (or safety.enable_auto_merge) is on.
1. Read agent/state/evolve/checkpoints.json. If missing/empty:
"No checkpoints recorded. Set safety.enable_rollback=true to enable." — exit.
2. Find the checkpoint where checkpoint.cycle == {cycle}.
If not found: "No checkpoint for cycle {cycle}. Available: {cycles}" — exit.
3. Confirm (typed phrase): print
"Roll back to cycle {cycle} (commit {sha})? This reverts every commit since
then and restores state. Type 'rollback {cycle}' to confirm:"
Accept ONLY the exact phrase; anything else: "Cancelled." — exit.
4. Re-check the HALT file.
5. Revert the repo. CAUTION: `git revert {sha}..HEAD` reverts each commit in the
range and **fails on any merge commit in that range** (it needs `-m`, and a
mixed range can't take a single `-m`) — leaving main HALF-reverted. This is
safe only on a linear history; auto-merge uses `--squash` precisely to keep it
linear. If the range may contain `--merge` merge commits, prefer `--hard`.
# default (linear history, e.g. squash-merged auto-merges):
git revert --no-edit {checkpoint.commit_sha}..HEAD
git push origin HEAD
# robust fallback / operator `--hard` (destructive — warn first):
git reset --hard {checkpoint.commit_sha} && git push --force origin HEAD
6. Restore confidence.json and action_queue.json pending items from
checkpoint.state_snapshot.
7. Create the HALT file: "Manual rollback to cycle {cycle} ({sha}). Delete to resume."
8. Print "Rolled back to cycle {cycle} ({sha}). HALT created — review, then
delete the HALT file to resume."
Step S — auto-merge {on|off}
Toggle the autonomous low-risk auto-merge opt-in (config.safety.enable_auto_merge, default off). This is the SINGLE switch that lets evolve merge PRs on its own.
1. Parse on|off. Unknown → print Usage and exit.
2. Turning ON:
- Require progressive_complexity.current_level == 3, else:
"Auto-merge needs Level 3. Run /ooda-config level 3 first." — exit.
- Print the gates and require a typed phrase:
"[DANGER] Auto-merge will let the agent merge LOW-RISK PRs without your click:
non-protected paths, ` for each:
1. config.json exists and is readable
2. Parses as valid JSON
3. `schema_version` field present
4. `project.name`, `project.locale`, `project.timezone` all present
5. `safety.halt_file`, `safety.confidence_threshold`, `safety.max_prs_per_cycle` present
6. Value ranges: `confidence_threshold` in [0.0, 1.0]; `max_prs_per_cycle` >= 1; `min_cycle_interval_minutes` >= 1
7. If present, `health_check_timeout_seconds` is a number in [2, 30]
8. If present, `test_timeout_seconds` is a number >= 10
9. If present, `deploy_monitor_timeout_seconds` is a number >= 60
10. If present, `deploy_health_wait_seconds` is a number >= 5
11. If present, `deploy_workflow_inputs` is a plain object (not array, not null)
12. If present, `safety.lock_timeout_minutes` is a number >= 1
13. At least one domain defined
14. Each domain has `weight`, `primary_skill`, `state_file`, and `status`
(or legacy `enabled` — warn `[WARN] domain {name}: no status field; evolve
treats enabled:true as status:"active"` instead of failing)
15. Every active domain's (`status == "active"`, or legacy `enabled: true`)
`primary_skill` is in `safety.skill_allowlist`
16. `progressive_complexity.current_level` is 0–3
17. No sensitive field holds a raw token (must use `$ENV_VAR` form)
18. At least one domain has `fallback: true` (confidence gate escape)
19. No duplicate `branch_prefix` values across domains
20. `signals.health_alert_bonus` should be 0 and `action_queue_decay_amount` in (0, 1]
23. If `implementation.enabled`, `progressive_complexity.current_level` must be 3
24. `memory.working_memory_size` >= 5 (minimum for pattern detection)
25. If `saturation` block present, `warn_threshold passed, failed`
On any failure append: `Run /ooda-config show to review your settings.`
---
## Usage
/ooda-config show Print current config summary /ooda-config level {0-3} Change progressive complexity level /ooda-config domain add {name} Add a new domain (interactive) /ooda-config domain remove {name} Disable a domain (soft-delete) /ooda-config domain list List all domains with status /ooda-config safety Show full safety settings /ooda-config validate Validate config.json structure and values /ooda-config lens review {domain} Show lens.json for a domain with evidence trails /ooda-config lens reset {domain} Wipe lens.json for a domain (start fresh) /ooda-config action list List pending actions with RICE and age /ooda-config action approve {id} Approve action for priority execution /ooda-config action defer {id} [N] Defer action for N days (default 7) /ooda-config action reject {id} Reject action with optional reason /ooda-config action prioritize {id} Move action to top of queue /ooda-config mode {name} Switch season/phase mode (alias: season {name}) /ooda-config season list List available season modes /ooda-config season show Show current mode overrides in detail /ooda-config rotation show {d} Show rotation list + cursor for a domain /ooda-config rotation reset {d} Reset rotation cursor to 0 /ooda-config context show Show active_context path + blob contents /ooda-config auto-merge {on|off} Toggle low-risk autonomous merge (Level 3, opt-in) /ooda-config rollback {cycle} Revert repo + state to a recorded checkpoint
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [mataeil](https://github.com/mataeil)
- **Source:** [mataeil/OODA-loop](https://github.com/mataeil/OODA-loop)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.