dev-cycle — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited dev-cycle (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The "builder" of the harness. Takes the highest-priority action from the action queue, implements it in a dedicated branch, verifies it passes tests, and creates a Draft PR for human review.
dev-cycle is the primary skill for the implementation domain. When evolve's Orient step selects implementation as the winning domain, it calls dev-cycle. All changes go through branch + PR — dev-cycle never commits directly to main.
(see "Auto-merge eligibility" below) AND the operator has opted in via config.safety.enable_auto_merge. Default behavior is Draft / human merge.
git add -A — only explicit file staging to prevent secret leaksauto-merge; dev-cycle only chooses draft vs ready.
progressive_complexity >= 3 or direct user invocation. Below Level 3, exit cleanly.config.safety.max_files_per_pr and config.safety.max_lines_per_pr. Exceeding either triggers a partial PR.config.safety.protected_paths forces Risk Tier 3 (already enforced — Draft only).git add {file} per file. git add -A is forbidden."blocked" and exit.if file exists at config.safety.halt_file:
Print "[HALT] dev-cycle stopped. Reason: {file_content}"
Print "Remove to resume: rm {config.safety.halt_file}"
EXIT immediately.Read config.json → progressive_complexity.current_level (authoritative source)
Also check config.json → implementation.enabled
if implementation.enabled == false AND not manually invoked by user:
Print "Implementation domain is disabled. Enable it: set implementation.enabled=true in config.json (done automatically by /ooda-config level 3)"
EXIT cleanly (not an error).
if progressive_complexity.current_level < 3 AND not manually invoked by user:
Print "Implementation requires Level 3. Current: {current_level}"
EXIT cleanly (not an error).Manual invocation means the user explicitly called /dev-cycle or /evolve in this session (as opposed to being triggered automatically by evolve on a schedule). When in doubt, treat as manual invocation and allow execution.
LEAP mode (v1.7.0). If evolve invoked this cycle with leap_mode=true (Step 3-K, fired by an artifact-quality plateau), do NOT pick the top-RICE feature. Instead:
weakest / below bar) — overhaul, rebuild, or refactor-for-cohesion of the core responsible for that dimension. RICE is bypassed on purpose: the whole point is to do the high-effort overhaul that RICE structurally suppresses.
config.leap.max_lines (default 1500) as the size budget for Step 3,instead of config.safety.max_lines_per_pr — a re-founding of the core is legitimately larger than a feature.
4-C2): the targeted dimension must rise by config.leap.min_dimension_delta or the change is reverted. The unit test still runs (information) but a unit-test failure alone does not block a leap (a restructured module may legitimately break a smoke test that mocked its old shape).
leap: overhaul {targeted_dimension} so it is legible as astep-change, not a feature. Then skip the RICE selection below and go straight to Step 2 with this overhaul as the selected action. FEATURE cycles (the default) proceed with RICE selection:
Read agent/state/evolve/action_queue.json.
Find the top pending item by highest effective_rice (after decay adjustment):
data = read action_queue.json as JSON object
if data is not a valid object:
Print "Action queue is empty or malformed. Nothing to implement."
EXIT cleanly.
# The canonical format uses {pending:[], in_progress:[], completed:[]}
# Read from data.pending (preferred). Fallback: if data.actions exists, filter
# items with status=="pending". Fallback: if data is a plain array, filter by status.
candidates = data.pending # or fallback as described above
if candidates is empty or not a list:
Print "No pending actions. Nothing to implement."
EXIT cleanly.
# Sort by effective_rice. If effective_rice is missing, fall back to rice_score.
for item in candidates:
if item.effective_rice is undefined:
item.effective_rice = item.rice_score * (1.0 - (item.decay_applied or 0.0))
selected = max(candidates, key=lambda x: x.effective_rice)
if selected.effective_rice <= 0:
Print "Top candidate RICE score is {selected.effective_rice} (≤ 0). Skipping."
EXIT cleanly.Print selected action details:
Selected action: {selected.title}
RICE score : {selected.effective_rice}
Source : {selected.source_domain}
Related : {selected.related_files}Update the action's status to "in_progress" in action_queue.json and write the file before proceeding. If the file write fails, abort and print the error.
Generate a URL-safe slug from the action title:
[a-z0-9-].use the action ID as the slug instead (e.g., action-001). If the action ID is also empty, generate a random 8-char hex string.
Get today's date in YYYYMMDD format.
git checkout -b auto/dev-cycle/{date}-{action-title-slug}If the branch already exists (e.g., retry or duplicate title), append -2, -3, etc., up to -9. If all suffixes are taken, abort with an error.
Print:
Branch: auto/dev-cycle/{date}-{action-title-slug}Read context files before writing any code:
CLAUDE.md exists at the project root, read it for project conventions.selected.related_files:"WARN: related file not found: {path} — skipping" and continue.related_files is empty or not set, print "INFO: No related files listed — proceeding with action title and source report only.".selected.source_domain report (if referenced) to understand themotivation behind this action.
Why this exists. The f1 dogfood proved the loop can "iterate without improving" — a maze/local-optimum — when generation is anchored to the model's own priors instead of to external ground truth. The fix (AlphaCodium arXiv:2401.08500, which raised pass@5 19%→44% with a structured pre-generation stage; AutoCodeRover; Simon Willison's "concrete examples beat abstract requirements"): ground every non-trivial change in an external reference BEFORE writing code. This is Boyd's Observe extended to the world's knowledge, not just local state.
For any leap / quality-improving / "make it better" action (skip for a trivial mechanical edit), BEFORE writing code:
config.references (and agent/state/research/*if present — a researched, cited playbook). Pick the reference target for this technique/domain (e.g. a named real-product level, a reference implementation URL, or a specific playbook move with its concrete API/parameters).
(the 30–50 lines that matter — the exact API calls, parameter values, order of operations), not the whole repo. If a research playbook already contains the cited concrete spec, use that.
(a) call X with params Y, (b) produce effect Z visible at camera/probe C, (c) not break the gate." Record them in the cycle's outcome as reference_block + acceptance_criteria.
real code. The PR/outcome records WHICH reference grounded it (grounded_in).
A leap with no grounded_in reference is a red flag for the maze: prefer researching a concrete approach over reaching for the model's first idea.
Analyze what needs to change based on the action title, source report, and the resolved reference block, then implement the changes (write and/or edit files).
Protected paths enforcement:
Before writing any file, check against config.safety.protected_paths. This prevents dev-cycle from modifying safety-critical files that could compromise the framework's integrity (self-modification prevention).
protected = config.safety.protected_paths -- e.g., ["agent/safety/*", "skills/evolve/*", "agent/contracts/*"]
before writing or editing any file:
for each pattern in protected:
if file path matches glob pattern:
Print "BLOCKED: {file} matches protected path '{pattern}'. Skipping."
Print "Protected paths cannot be modified by dev-cycle, even at Level 3."
Add to PR body notes: "⚠ Protected path {file} was NOT modified (blocked by safety policy)."
Set protected_blocked = true -- forces Draft / Risk Tier 3 below (#35)
DO NOT write/edit this file — continue to next file.If ALL planned files are protected, mark the action as "blocked" with memo "All target files are protected paths" and EXIT cleanly.
If protected_blocked is true (some — not all — target files were protected and skipped), the PR is never auto-merge-eligible even if the remaining diff is small and green: a partial change with safety-critical files silently dropped may be incomplete or incoherent, so a human must review it (#35).
Size limit enforcement:
Track changes as you write. After each file edit, run git diff --stat on the working tree to get authoritative counts (do not estimate):
files_changed = 0
lines_changed = 0 # counted as (additions + deletions) from git diff --numstatBefore writing each file:
if files_changed >= config.safety.max_files_per_pr:
Print "PR size limit reached ({max_files_per_pr} files). Creating partial PR."
Print "Remaining work noted in action-queue memos."
Add memo to action-queue: "Partial implementation — {files_changed} files changed."
GOTO Step 4 (verify what was done so far)
if lines_changed + estimated_lines_for_this_file > config.safety.max_lines_per_pr:
Print "PR line limit reached ({max_lines_per_pr} lines). Creating partial PR."
GOTO Step 4When partial: create a NEW pending action for the unfinished scope (same source_domain, title "{original title} (remainder)", rice_score inherited) and note the split in the original action's memos field. The original action then proceeds to "proposed" like any other PR — the remainder is independently selectable next cycle and cannot be silently lost with the original stuck in_progress.
Gate integrity (v1.10.1 — earned by the f1 probe). A static check is necessary but NOT sufficient, and a sub-agent's self-reported gate result is not trustworthy — the orchestrator must verify from facts. Two real misses the f1 overnight run surfaced, BOTH caught only by loading the artifact in its real runtime, never by the unit gate: 1.node --checkexits 0 on a same-scopeconstREDECLARATION that the browser ES-module parser rejects — the game wouldn't boot, yet the cycle'snode --check + smokegate "passed". For an ES-module/browser artifact, also do a module-load check (import the changed modules in their module system, e.g.node --input-type=module -e 'import("./src/x.js")', or load the page) — that catches whatnode --checkcannot. 2. A cumulative visual regression (over-exposed-to-white frame) passed every unit gate; only a rendered critique caught it. Rule: for rich-runtime artifacts (browser/UI/graphics/game), the verification MUST load the artifact the way its runtime does (module-load + render/screenshot critique, i.e. evolve Step 5-G), and evolve re-checks the gate from recorded facts — it does NOT take the build skill's word for "tests passed".
If `config.test_command` is not configured or is empty:
Print "No test_command configured. Skipping tests."
test_status = "skipped"
GOTO Step 5If configured, run tests with timeout enforcement:
timeout {config.test_timeout_seconds or 300}s {config.test_command}If the test command exceeds the timeout, treat as failure:
if exit_code == 124 (timeout):
Print "ERROR: Test command timed out after {timeout}s. Treating as failure."
test_output = "Test timeout after {timeout}s"Track attempts:
attempt = 1
max_attempts = 3
timeout = config.test_timeout_seconds or 300
while attempt <= max_attempts:
run test_command with timeout
if exit_code == 0:
test_status = "passed"
break
else:
Print "Tests failed (attempt {attempt}/{max_attempts})."
if attempt < max_attempts:
Print "Attempting fix..."
targeted_fix(test_output):
1. Parse test runner output for the first failing test name and assertion message.
2. Read the source file containing the failing assertion.
3. Apply a single-location edit (≤ 15 lines) that addresses the assertion.
4. Do NOT modify test files — only fix production code.
5. If the failure is an import/module error, fix the import only.
attempt += 1
if test_status != "passed" after 3 attempts:
Print "[BLOCKED] Tests failed after 3 attempts. Action marked as blocked."
Print "Review test output above and fix manually."
Set action status to "blocked" and MOVE it to completed[] in action_queue.json
(pending[] holds only workable items; evolve's 6-C6 hygiene sweep enforces the
same rule — a blocked item must never sit in pending/in_progress forever)
git stash (preserve work without committing)
git checkout main (never leave the session on the dead feature branch —
the next evolve cycle's git operations assume main)
EXIT with non-zero status.When fixing between attempts: make targeted changes only — address the specific failing assertion or import error. Do not rewrite large sections.
Stage only the files that were explicitly changed in Step 3 and any files edited during test-fix retries in Step 4. Maintain a cumulative changed_files list across both steps.
Never use git add -A or git add ..
# Verify each file exists before staging (skip deleted files with a warning)
for file in changed_files:
if file exists on disk:
git add {file}
else:
Print "WARN: {file} no longer exists — skipping stage"
git commit -m "{selected.title}"
git push origin HEADIf git push fails:
if error contains "conflict" or "rejected" or "non-fast-forward":
Print "ERROR: merge conflict detected — {error}"
Print "Resolve manually, then: git push origin {branch_name}"
Update action status to "blocked" with memo "merge conflict with main"
else:
Print "ERROR: git push failed — {error}"
Print "Push manually with: git push origin {branch_name}"
Record push error in action-queue memos.
EXIT with non-zero status.Auto-merge eligibility (compute before creating the PR). The PR is auto-merge-eligible iff ALL of these hold:
config.safety.enable_auto_merge == true -- opt-in, default false
AND config.progressive_complexity.current_level >= 3
AND no changed file matches config.safety.protected_paths
AND protected_blocked == false -- no protected file was skipped (#35)
AND changed_files_count <= config.safety.auto_merge_max_files -- default 5
AND changed_lines_count <= config.safety.auto_merge_max_lines -- default 100
AND test_status == "passed" -- the CANONICAL Step-4 value;
-- "skipped" (no test_command)
-- is NOT eligible — auto-merge
-- requires actually-green testsIf NOT eligible (the default), create the PR as Draft. If eligible, create it ready (omit --draft) and stamp auto_merge_eligible=true in the meta comment so evolve 4-C can recognize it — evolve still independently re-checks every gate before merging (defense in depth).
Create the PR (--draft UNLESS auto-merge-eligible):
gh pr create \
--title "{selected.title}" \
$([ "$auto_merge_eligible" = true ] || echo --draft) \
--body "$(cat <<'EOF'
<!-- ooda:meta source_domain={selected.source_domain} rice={selected.effective_rice} action_id={selected.id} auto_merge_eligible={true|false} protected_blocked={true|false} -->
## Source
- **Domain**: {selected.source_domain}
- **RICE Score**: {selected.effective_rice}
- **Action ID**: {selected.id}
## Changes
| File | Description |
|------|-------------|
| `{file1}` | {one-line description} |
## Test Results
- **Status**: {test_status}
- **Command**: `{config.test_command}`
- **Attempts**: {attempt}/{max_attempts}
- **Output** (last run): `{last 5 lines of test output or "tests skipped"}`
## Notes
{any partial PR notes, size limit warnings, or protected-path flags}
---
Generated by OODA-loop dev-cycle v1.0.0
EOF
)"(--draft is added by the conditional on the gh pr create line above — present unless the change is auto-merge-eligible.)
If gh is not available:
Print "gh (GitHub CLI) not found. PR creation skipped."
Print "Push the branch and create a PR manually:"
Print " git push origin {branch_name}"
Print " gh pr create --draft --title \"{selected.title}\""
pr_number = nullUpdate action_queue.json:
{
"status": "proposed",
"pr_number": {number or null},
"pr_url": "{url or null}",
"proposed_at": "{ISO 8601}"
}Print the final summary:
dev-cycle complete — {ISO timestamp}
Action : {selected.title} (RICE: {selected.effective_rice})
Branch : auto/dev-cycle/{slug}
PR : #{pr_number} ({Draft|ready}) | {pr_url}
Files : {files_changed} changed
Lines : {lines_changed} changed
Tests : {test_status}
Status : proposed
pr_created : {true|false}pr_created is a REQUIRED report variable (true iff a PR was actually opened this run) — it is what evolve's 4-B evaluates for this skill's chain trigger (pr_created == true). Report variables are the evaluation source for skills whose contract output file (here action_queue.json) doesn't carry the condition fields at top level.
If PR was not created (gh unavailable):
PR : not created — push branch and create manually| Scenario | Behavior |
|---|---|
| HALT file present | Print reason, exit immediately |
| Level < 3, not manual | Print level message, exit cleanly |
| action_queue.json missing | Print "Action queue not found at agent/state/evolve/action_queue.json", exit cleanly |
| No pending actions | Print "No pending actions", exit cleanly |
| Branch already exists | Append suffix (-2, -3), continue |
| PR size limit hit | Create partial PR, note remaining scope in action memos |
| Tests fail after 3 tries | Mark action "blocked", stash changes, exit non-zero |
git push fails | Record error in memos, print manual instructions, exit non-zero |
gh not installed | Skip PR creation, print manual instructions, exit 0 |
test_command not configured | Skip tests, record "skipped", continue to PR |
related_files missing/empty | Proceed with action title and source report only |
| Protected path changed | Already in Draft mode — note in PR body as protected-path change |
| Merge conflict on push | Abort push, mark action "blocked" with memo "merge conflict with main", stash changes, exit non-zero |
action_queue.json malformed | Print parse error, exit cleanly |
Branch suffix exhausted (-9) | Print error, mark action "blocked", exit non-zero |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.