agentleFS
dynos-fitdynos-fit/dynos-work/skills/residual/SKILL.md

residual

Skill2 starsChanged 8 months agoMITView on GitHub

Inspect and drain the proactive-residual queue at .dynos/proactive-findings.json. Subcommands: list, run-next. Runs only when the user explicitly types /dynos-work:residual; never auto-triggered from conversation.

The file

Show its rules and text

We split this skill into the rules it gives an agent

18 rules. About 0 are common, 3 are distinctive, and 15 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 15

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Run the following Python snippet.

    line 42
  2. Very specific:

    If proactive-findings.json does not exist, print exactly dynos-work: no residuals queued and exit 0.

    line 97
  3. Very specific:

    If the file exists but findings is [], print exactly dynos-work: no residuals queued and exit 0.

    line 99
  4. Very specific:

    If the file has rows but every row is done and --all was not passed, print exactly dynos-work: no active residuals (use --all to show done …

    line 101
  5. Very specific:

    Default behavior excludes done rows.

    line 109
  6. Very specific:

    Before picking a new row, scan the queue for any row whose status is "in_progress".

    line 129
  7. Very specific:

    Use lib_residuals.load_queue(lib_residuals.queue_path(root)).

    line 147
  8. Very specific:

    Use lib_residuals.select_next_pending(root).

    line 153
  9. Distinctive:

    If the call raises an exception (filesystem error, race with another writer, etc.), print the error to stderr and exit non-zero.

    line 168
  10. Very specific:

    Before invoking /dynos-work:start, the new task directory does not yet exist.

    line 209
  11. Very specific:

    Read .dynos/task-{id}/manifest.json.

    line 292
  12. Very specific:

    If stage is "DONE" or "FAILED", polling is over — proceed to step 6.

    line 293
  13. Distinctive:

    If the manifest is missing or unreadable for more than a few consecutive polls (suggesting the spawned task crashed before writing the …

    line 298
  14. Very specific:

    After a /dynos-work:audit run you want to drain newly-surfaced proactive residual findings.

    line 387
  15. Very specific:

    Do NOT spawn auditor or executor agents directly to "fix" a residual.

    line 395
  16. Very specific:

    Do NOT skip the polling loop in step 5.

    line 398
  17. Very specific:

    Do NOT mutate proactive-findings.json outside of lib_residuals.update_row_status (or an equivalent LOCK_EX atomic write).

    line 400
  18. Distinctive:

    Do NOT JSON-wrap the stdout output.

    line 404

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

dynos-work: Residual

Inspect and drain the proactive-residual queue maintained at {root}/.dynos/proactive-findings.json (where {root} is the current project root — the directory that contains .dynos/). Two subcommands are supported: list and run-next.

Ruthlessness Standard

  • The queue file is the source of truth. Do not derive queue state from memory, prior conversation, or vibes — re-read proactive-findings.json on every invocation.
  • run-next MUST NOT spawn auditor or executor agents directly. The only execution path is /dynos-work:start. Any other route bypasses the lifecycle, the audit gate, and the round-trip update in cmd_run_audit_finish.
  • Status writes go through hooks/lib_residuals.update_row_status (or an equivalent LOCK_EX atomic write). Never edit the JSON file by hand or with a partial overwrite — concurrent producers (other audits finishing) can clobber a non-atomic write.
  • Polling is mandatory. A spawned /dynos-work:start task is not done the moment it returns control. Only the manifest.json stage field tells you when it is finished.

Subcommand: list

Invoked in two forms:

  • /dynos-work:residual list — default view, excludes done rows.
  • /dynos-work:residual list --all — includes all rows (including done).

What you do

  1. Resolve {root} — the current project root (the directory containing .dynos/). If running from a repo subdirectory, walk upward until .dynos/ is found.
  2. Run the following Python snippet. To include done rows, pass --all as a positional argument to the heredoc process — bash makes it visible to the snippet via sys.argv:

    # Default (excludes done rows):
    python3 - <<'PY'
    ...
    PY
    
    # Include all rows (including done):
    python3 - --all <<'PY'
    ...
    PY

    The shared snippet body:

    python3 - <<'PY'
    from pathlib import Path
    import sys
    sys.path.insert(0, str(Path("hooks").resolve()))
    import lib_residuals
    root = Path(".").resolve()
    queue = lib_residuals.queue_path(root)
    data = lib_residuals.load_queue(queue)
    findings = data.get("findings", [])
    raw_count = len(findings)
    include_done = "--all" in sys.argv
    findings = [r for r in findings if include_done or r.get("status") != "done"]
    if not findings:
        if raw_count == 0:
            print("dynos-work: no residuals queued")
        else:
            print("dynos-work: no active residuals (use --all to show done rows)")
        sys.exit(0)
    # Sort oldest first by created_at
    findings_sorted = sorted(findings, key=lambda r: r.get("created_at", ""))
    for r in findings_sorted:
        title = r.get("title", "") or ""
        if len(title) > 60:
            title = title[:60]
        print(
            f"{r.get('id','')}\t"
            f"{r.get('status','')}\t"
            f"attempts={r.get('attempts',0)}\t"
            f"{r.get('source_auditor','')}\t"
            f"{title}\t"
            f"{r.get('created_at','')}"
        )
    PY
  3. Output rules — these are CONTRACTUAL:
    • If proactive-findings.json does not exist, print exactly dynos-work: no residuals queued and exit 0.
    • If the file exists but findings is [], print exactly dynos-work: no residuals queued and exit 0.
    • If the file has rows but every row is done and --all was not passed, print exactly dynos-work: no active residuals (use --all to show done rows) and exit 0.
    • Otherwise print one tab-separated line per row containing, in order: id, status, attempts, source_auditor, title (truncated to 60 chars), created_at. All six fields must appear on every row.
    • Default behavior excludes done rows. Pass --all to include them. pending, in_progress, wontfix, and failed rows always appear regardless of --all.
    • Plain text only. No JSON, no ANSI colour codes, no header row decoration that would break the six-field shape.

Edge cases

  • Corrupt or unreadable proactive-findings.json: lib_residuals.load_queue treats this as an empty queue and returns {"findings": []}. The command therefore prints dynos-work: no residuals queued rather than raising. If you want to surface the corruption to the user, print a separate stderr warning (do not change the stdout shape).

Subcommand: run-next

Invoked as /dynos-work:residual run-next.

Step 0 — In-progress guard (do this BEFORE selecting a row)

Before picking a new row, scan the queue for any row whose status is "in_progress". If at least one such row exists, another run-next invocation (or a previously-spawned residual task that has not yet completed its round-trip) is already in flight. In that case, print exactly:

dynos-work: residual in_progress — run-next already active

and exit 0. Do NOT select another row, do NOT mutate the queue, do NOT spawn /dynos-work:start. This guard prevents two concurrent run-next fires from double-selecting and lets a stuck round-trip be resolved (manually or by cmd_run_audit_finish) before another attempt is consumed.

Step 1 — Load the queue

Use lib_residuals.load_queue(lib_residuals.queue_path(root)). If the result has an empty findings list (file missing or empty), print exactly dynos-work: residual queue empty and exit 0.

Step 2 — Select the oldest eligible row

Use lib_residuals.select_next_pending(root). This returns the oldest row (by created_at ascending) whose status == "pending" AND attempts < 3. A row whose status == "in_progress" is NEVER eligible (it is already in flight). If the function returns None, print exactly dynos-work: residual queue empty and exit 0.

Step 3 — Mark the row in_progress (atomic)

Call lib_residuals.update_row_status(root, row["id"], "in_progress"). This is a LOCK_EX read-modify-write that:

  • Sets status = "in_progress".
  • Increments attempts by 1.
  • Sets last_attempt_at to the current ISO 8601 UTC timestamp.

If the call raises an exception (filesystem error, race with another writer, etc.), print the error to stderr and exit non-zero. DO NOT proceed to step 4 or step 5 — the queue is in an unknown state and a spawned task without a corresponding in_progress row would lose its round-trip update.

NOTE: update_row_status from the lib only changes status (and sets last_attempt_at on the in_progress transition); it does NOT increment attempts. Increment attempts in the same critical section by either calling a helper that wraps both, or by reading the row, mutating both fields, and writing back via the same LOCK_EX path. The key invariant is: when control returns from step 3, the row on disk has status == "in_progress" AND attempts incremented AND last_attempt_at updated, all visible to subsequent readers.

Recommended Python snippet for step 3:

python3 - <<'PY'
from pathlib import Path
import sys, json
sys.path.insert(0, str(Path("hooks").resolve()))
import lib_residuals
root = Path(".").resolve()
row = lib_residuals.select_next_pending(root)
if row is None:
    print("dynos-work: residual queue empty")
    sys.exit(0)
# Guard: any in_progress already?
data = lib_residuals.load_queue(lib_residuals.queue_path(root))
if any(r.get("status") == "in_progress" for r in data.get("findings", [])):
    print("dynos-work: residual in_progress — run-next already active")
    sys.exit(0)
lib_residuals.update_row_status(root, row["id"], "in_progress")
# Emit the row payload so the orchestrator can build the start input.
print(json.dumps({"id": row["id"], "description": row.get("description","")}))
PY

Step 4 — Set auto-approve gates for the new task (best-effort)

Before invoking /dynos-work:start, the new task directory does not yet exist. The auto-approve-gates ctl command requires the new task's manifest.json to be on disk because it is the sole writer of manifest.auto_approve_gates. There is therefore a sequencing constraint: set-auto-approve-gates MUST be called AFTER /dynos-work:start Step 0 has created .dynos/task-{new_id}/manifest.json but BEFORE classification has been settled (the gate refuses false → true once manifest.classification is non-null — this is the AC-4 enforcement in cmd_set_auto_approve_gates).

In practice, call set-auto-approve-gates immediately after /dynos-work:start returns control with the new task_id. If the spawned start skill has already advanced past classification by the time you make the call, the gate will refuse with a non-zero exit; in that case fall through to the normal human-approval path (the gates were not enabled, but the residual still runs).

"${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/bin/dynos" ctl set-auto-approve-gates \
  --task-dir .dynos/task-{new_id} \
  --from-residual-id {row["id"]}

Fall-through behavior:

  • Exit code 0: manifest.auto_approve_gates was set to true on the new task. Subsequent SPEC_REVIEW / PLAN_REVIEW / TDD_REVIEW gates in /dynos-work:start will auto-approve via the receipt path. Continue polling in Step 5.
  • Exit code 1: the gate refused (a pick-time ceiling tripped, the classification was already settled, the manifest is missing, or the global policy file disabled auto-approval). Log the stderr message and continue with normal human-approval gates. DO NOT abort the run-next flow — the residual must still run; it just runs with a human in the loop.

Recommended logging (through ctl; never append to execution-log.md directly):

"${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/bin/dynos" ctl log-append .dynos/{new_id} --tag GATE --message "set-auto-approve-gates residual={row.id} task={new_id} result={ok|refused: <reason>}"

The auto-approval pathway is an opportunistic acceleration of operator-trusted residuals. A refusal is a normal outcome (e.g. for high-risk security findings) and never blocks the residual from running.

Step 5 — Invoke /dynos-work:start and POLL until terminal stage

Build the input text for /dynos-work:start. The first line MUST be the residual-id sentinel comment, followed by a blank line, followed by the row's description:

<!-- residual-id: {row.id} -->

{row.description}

This sentinel is what cmd_run_audit_finish greps for via lib_residuals.extract_residual_id to perform the round-trip status update when the spawned task hits a terminal stage.

Invoke /dynos-work:start with that text. The spawned task is a NEW dynos-work task with its own task-{id} directory under .dynos/ and its own manifest.json.

WAIT — do NOT continue to step 6 until you have read the spawned task's manifest.json and confirmed stage is exactly "DONE" or "FAILED". Reading the manifest once and assuming the task completed is INCORRECT. The /dynos-work:start skill returns control to you long before the task is finished — it only kicks off the lifecycle. The actual work proceeds through PLANNING, EXECUTION, AUDIT, and REPAIR stages, which can take many minutes.

Polling protocol (mandatory)

  1. Note the new task's directory .dynos/task-{id}/ from the /dynos-work:start output. (If the start skill does not echo the id, inspect .dynos/ for the most recent task-* directory whose manifest.json references this residual via the sentinel — but prefer reading the id from the start skill's output to avoid ambiguity.)
  2. Read .dynos/task-{id}/manifest.json. Inspect the stage field.
  3. If stage is "DONE" or "FAILED", polling is over — proceed to step 6.
  4. Otherwise, sleep approximately 30 seconds and re-read the manifest. Repeat. The lifecycle is monotonic; the manifest will eventually reach a terminal stage.
  5. If the manifest is missing or unreadable for more than a few consecutive polls (suggesting the spawned task crashed before writing the manifest), give up polling, treat this as a round-trip-missing case, and fall through to step 6 — the fallback path will revert the row to pending.

The skill prose deliberately uses 30-second polling rather than busy waiting. The lifecycle stages take seconds to minutes; polling tighter than every few seconds wastes CPU and risks rate-limiting the filesystem. Polling looser than a minute slows feedback for the operator. 30 seconds is the recommended cadence.

Step 6 — Read the queue and report the round-trip outcome

Once the spawned task is at a terminal stage (or polling has been abandoned because the manifest is permanently missing), re-read the queue:

data = lib_residuals.load_queue(lib_residuals.queue_path(root))
row = next((r for r in data["findings"] if r["id"] == residual_id), None)

Then dispatch on row["status"]:

Observed statusMeaningOutput
"done"cmd_run_audit_finish saw the sentinel and applied the round-trip update on a successful audit.residual {id} → done
"failed"cmd_run_audit_finish recorded a third FAILED attempt (attempts >= 3 after the third failure).residual {id} → failed
"pending" and attempts < 3The spawned task FAILED but more attempts are allowed; cmd_run_audit_finish set status back to "pending" for the next run-next to retry.residual {id} → pending
"in_progress"The round-trip update did NOT happen. The orchestrator crashed, the manifest never reached DONE/FAILED, or cmd_run_audit_finish did not see the sentinel. The attempt is sacrificed.Revert the row to "pending" WITHOUT incrementing attempts (call update_row_status(root, id, "pending"); do NOT touch attempts). Print residual {id} → pending (round-trip missing, reverted).

Exit 0 in all four cases. The queue is now in a consistent state for the next run-next invocation.

Fallback (round-trip missing) — important details

The "still in_progress at terminal" case represents a real failure mode: a spawned residual task whose lifecycle finished but whose queue row was never updated. Possible causes include:

  • The orchestrator process died between manifest stage = DONE and the cmd_run_audit_finish round-trip write.
  • The spawned task wrote a manifest but cmd_run_audit_finish did not recognise the sentinel (e.g. malformed first line — but extract_residual_id is anchored, so this is rare).
  • Polling was abandoned because the manifest was unreadable for many consecutive reads.

In all these cases, the attempt is sacrificed — we do NOT increment attempts further on revert, because the original update_row_status in step 3 already incremented it once. (Steps 4 and 5 do not touch attempts.) Reverting status to "pending" lets the next run-next retry. After three sacrificed attempts the row will not be eligible for selection (attempts < 3 guard in select_next_pending), and an operator must inspect it manually.

To revert without touching attempts, call:

lib_residuals.update_row_status(root, residual_id, "pending")

update_row_status is documented to change ONLY status (and last_attempt_at on the in_progress transition). It does not modify attempts. The increment that already happened in step 3 stays.

Output contract summary

ConditionOutput (exact)Exit
list: queue file missing or findings == []dynos-work: no residuals queued0
list: queue file has rows but all are done (default view)dynos-work: no active residuals (use --all to show done rows)0
list: rows presentone tab-separated line per row (id, status, attempts, source_auditor, title[60], created_at)0
run-next: queue file missing or findings == [] or no eligible rowdynos-work: residual queue empty0
run-next: another row already in_progressdynos-work: residual in_progress — run-next already active0
run-next: spawned task → row.status == "done"residual {id} → done0
run-next: spawned task → row.status == "failed"residual {id} → failed0
run-next: spawned task → row.status == "pending" and attempts < 3residual {id} → pending0
run-next: spawned task → row.status still "in_progress"revert to "pending", print residual {id} → pending (round-trip missing, reverted)0
run-next: step-3 atomic write failederror message to stderrnon-zero

Plain text only. No JSON wrapping. No ANSI colour. The list output must be parseable by awk '{print $1}' style scripts (tabs separate fields).

When to use

  • After a /dynos-work:audit run you want to drain newly-surfaced proactive residual findings.
  • As the body of a periodic loop that calls run-next until the output is dynos-work: residual queue empty.
  • To inspect the queue before deciding whether to drain (list).

What NOT to do

  • Do NOT spawn auditor or executor agents directly to "fix" a residual. Only /dynos-work:start is the execution path; it enforces the full lifecycle and the audit gate.
  • Do NOT skip the polling loop in step 5. Reading manifest.json once and assuming completion is INCORRECT.
  • Do NOT mutate proactive-findings.json outside of lib_residuals.update_row_status (or an equivalent LOCK_EX atomic write). Concurrent audits writing to the queue will clobber a non-atomic write.
  • Do NOT JSON-wrap the stdout output. The output contract is plain bare strings; tests (and operators) compare them by exact equality.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.