@reddb-io/red-skills-dev 2.75.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +30 -0
  3. package/package.json +32 -0
  4. package/skills/engineering/README.md +36 -0
  5. package/skills/engineering/_report-runtime/WRAPPER.md +47 -0
  6. package/skills/engineering/afk/AGENT-PROMPT.md +295 -0
  7. package/skills/engineering/afk/SAFETY.md +103 -0
  8. package/skills/engineering/afk/SKILL.md +160 -0
  9. package/skills/engineering/afk/TROUBLESHOOTING.md +260 -0
  10. package/skills/engineering/afk/actions-lane.md +213 -0
  11. package/skills/engineering/afk/bin/README.md +29 -0
  12. package/skills/engineering/afk/bin/afk.mjs +51 -0
  13. package/skills/engineering/afk/defaults/cargo-pre-worktree.sh +37 -0
  14. package/skills/engineering/afk/defaults/envelope-post-attempt.sh +47 -0
  15. package/skills/engineering/afk/defaults/gradle-pre-worktree.sh +42 -0
  16. package/skills/engineering/afk/defaults/heartbeat-post-attempt.sh +43 -0
  17. package/skills/engineering/afk/defaults/validation-post-merge.sh +77 -0
  18. package/skills/engineering/afk/detectors/README.md +72 -0
  19. package/skills/engineering/afk/detectors/cargo.sh +27 -0
  20. package/skills/engineering/afk/detectors/gradle.sh +35 -0
  21. package/skills/engineering/afk/docs/BOOT-SWEEPS.md +81 -0
  22. package/skills/engineering/afk/docs/CONFIG.md +238 -0
  23. package/skills/engineering/afk/docs/ENVELOPE.md +177 -0
  24. package/skills/engineering/afk/docs/HANDOFF.md +70 -0
  25. package/skills/engineering/afk/docs/LIVENESS.md +56 -0
  26. package/skills/engineering/afk/docs/OPERATIONS.md +409 -0
  27. package/skills/engineering/afk/examples/only-mine.sh +32 -0
  28. package/skills/engineering/afk/examples/red-afk-attempt-action.yml +53 -0
  29. package/skills/engineering/afk/examples/rs-afk-attempt.yml +76 -0
  30. package/skills/engineering/afk/fleet.md +127 -0
  31. package/skills/engineering/afk/hooks/red-cargo +44 -0
  32. package/skills/engineering/afk/hooks/red-envelope +50 -0
  33. package/skills/engineering/afk/hooks/red-gradle +50 -0
  34. package/skills/engineering/afk/hooks/red-heartbeat +46 -0
  35. package/skills/engineering/afk/hooks/red-validation +79 -0
  36. package/skills/engineering/afk/monitor.md +170 -0
  37. package/skills/engineering/afk/runner-claude-minimax.md +71 -0
  38. package/skills/engineering/afk/runner-claude.md +67 -0
  39. package/skills/engineering/afk/runner-codex.md +138 -0
  40. package/skills/engineering/afk/runner-hermes.md +40 -0
  41. package/skills/engineering/afk/runner-opencode.md +142 -0
  42. package/skills/engineering/ask-red/SKILL.md +137 -0
  43. package/skills/engineering/audit-skills/SKILL.md +46 -0
  44. package/skills/engineering/code-review/SKILL.md +106 -0
  45. package/skills/engineering/context/SKILL.md +142 -0
  46. package/skills/engineering/curate/SKILL.md +133 -0
  47. package/skills/engineering/daily-review/SKILL.md +68 -0
  48. package/skills/engineering/dashboard/SKILL.md +44 -0
  49. package/skills/engineering/diagnose/SKILL.md +155 -0
  50. package/skills/engineering/diagnose/scripts/hitl-loop.template.sh +41 -0
  51. package/skills/engineering/go/SKILL.md +144 -0
  52. package/skills/engineering/go/TROUBLESHOOTING.md +81 -0
  53. package/skills/engineering/ground-truth/SKILL.md +84 -0
  54. package/skills/engineering/hitl/SKILL.md +171 -0
  55. package/skills/engineering/hitl/TROUBLESHOOTING.md +76 -0
  56. package/skills/engineering/implement/SKILL.md +69 -0
  57. package/skills/engineering/improve-codebase-architecture/DEEPENING.md +37 -0
  58. package/skills/engineering/improve-codebase-architecture/INTERFACE-DESIGN.md +44 -0
  59. package/skills/engineering/improve-codebase-architecture/LANGUAGE.md +53 -0
  60. package/skills/engineering/improve-codebase-architecture/SKILL.md +81 -0
  61. package/skills/engineering/model-tier-policy/SKILL.md +83 -0
  62. package/skills/engineering/prototype/LOGIC.md +79 -0
  63. package/skills/engineering/prototype/SKILL.md +30 -0
  64. package/skills/engineering/prototype/UI.md +112 -0
  65. package/skills/engineering/red-doctor/APPLY.md +35 -0
  66. package/skills/engineering/red-doctor/SKILL.md +172 -0
  67. package/skills/engineering/red-gains/SKILL.md +49 -0
  68. package/skills/engineering/red-setup/INTERVIEW.md +266 -0
  69. package/skills/engineering/red-setup/ISSUE-SWEEP.md +18 -0
  70. package/skills/engineering/red-setup/REFERENCE.md +35 -0
  71. package/skills/engineering/red-setup/SKILL.md +58 -0
  72. package/skills/engineering/red-setup/WORKFLOWS.md +74 -0
  73. package/skills/engineering/red-setup/WRITE-CONTRACT.md +151 -0
  74. package/skills/engineering/red-setup/config-template.yaml +174 -0
  75. package/skills/engineering/red-setup/domain.md +52 -0
  76. package/skills/engineering/red-setup/issue-tracker-github.md +88 -0
  77. package/skills/engineering/red-setup/rsp-retirement-runbook.md +35 -0
  78. package/skills/engineering/red-setup/scripts/install-runtime-shim.sh +189 -0
  79. package/skills/engineering/red-setup/triage-labels.md +176 -0
  80. package/skills/engineering/red-setup/workflows/red-issues-needs-triage.yml +36 -0
  81. package/skills/engineering/red-statusline/HOST-NOTES.md +180 -0
  82. package/skills/engineering/red-statusline/SKILL.md +58 -0
  83. package/skills/engineering/report-bug/SKILL.md +155 -0
  84. package/skills/engineering/resolving-merge-conflicts/SKILL.md +143 -0
  85. package/skills/engineering/retake/SKILL.md +184 -0
  86. package/skills/engineering/review-adrs/SKILL.md +125 -0
  87. package/skills/engineering/start/ADR-FORMAT.md +47 -0
  88. package/skills/engineering/start/CONTEXT-FORMAT.md +77 -0
  89. package/skills/engineering/start/DOC-LANDING-FINALIZER.md +9 -0
  90. package/skills/engineering/start/SKILL.md +179 -0
  91. package/skills/engineering/tdd/SKILL.md +96 -0
  92. package/skills/engineering/tdd/deep-modules.md +33 -0
  93. package/skills/engineering/tdd/interface-design.md +31 -0
  94. package/skills/engineering/tdd/mocking.md +59 -0
  95. package/skills/engineering/tdd/tests.md +85 -0
  96. package/skills/engineering/to-spec/SKILL.md +104 -0
  97. package/skills/engineering/to-tickets/SKILL.md +158 -0
  98. package/skills/engineering/triage/AGENT-BRIEF.md +207 -0
  99. package/skills/engineering/triage/OUT-OF-SCOPE.md +101 -0
  100. package/skills/engineering/triage/SKILL.md +176 -0
  101. package/skills/engineering/verify/SKILL.md +136 -0
  102. package/skills/engineering/wayfinder/SKILL.md +128 -0
  103. package/skills/engineering/zoom-out/SKILL.md +51 -0
  104. package/skills/engineering/zoom-out/scripts/graph-mode-plumbing.sh +36 -0
  105. package/skills/engineering/zoom-out/scripts/tests/contract.test.sh +168 -0
  106. package/skills/knowledge/README.md +5 -0
  107. package/skills/knowledge/research/SKILL.md +76 -0
  108. package/skills/misc/README.md +8 -0
  109. package/skills/misc/branch-lock/SKILL.md +187 -0
  110. package/skills/misc/branch-lock/scripts/branch-lock-hook.sh +113 -0
  111. package/skills/misc/branch-lock/scripts/branch-lock-session-start.sh +81 -0
  112. package/skills/misc/branch-lock/scripts/branch-lock.sh +68 -0
  113. package/skills/misc/branch-lock/scripts/lib/dev-config.sh +126 -0
  114. package/skills/misc/branch-lock/scripts/lib/git-command-classifier.sh +198 -0
  115. package/skills/misc/branch-lock/scripts/lib/lock-store.sh +67 -0
  116. package/skills/misc/branch-lock/scripts/lib/scope-resolver.sh +43 -0
  117. package/skills/misc/branch-lock/scripts/tests/branch-lock-cli.test.sh +114 -0
  118. package/skills/misc/branch-lock/scripts/tests/claude-plugin-hook.test.sh +117 -0
  119. package/skills/misc/branch-lock/scripts/tests/codex-hook.test.sh +229 -0
  120. package/skills/misc/branch-lock/scripts/tests/dev-config.test.sh +99 -0
  121. package/skills/misc/branch-lock/scripts/tests/git-command-classifier.test.sh +187 -0
  122. package/skills/misc/branch-lock/scripts/tests/lock-store.test.sh +90 -0
  123. package/skills/misc/branch-lock/scripts/tests/scope-resolver.test.sh +63 -0
  124. package/skills/misc/branch-lock/scripts/tests/session-start.test.sh +123 -0
  125. package/skills/misc/git-guardrails-claude-code/SKILL.md +105 -0
  126. package/skills/misc/git-guardrails-claude-code/scripts/block-dangerous-git.sh +231 -0
  127. package/skills/misc/git-guardrails-claude-code/scripts/tests/block-dangerous-git.test.sh +143 -0
  128. package/skills/misc/migrate-to-shoehorn/SKILL.md +96 -0
  129. package/skills/misc/setup-pre-commit/SKILL.md +94 -0
  130. package/skills/productivity/README.md +8 -0
  131. package/skills/productivity/ff/SKILL.md +116 -0
  132. package/skills/productivity/handoff/SKILL.md +37 -0
  133. package/skills/productivity/reflect/SKILL.md +18 -0
  134. package/skills/productivity/write-a-skill/SKILL.md +190 -0
  135. package/skills/productivity/write-a-skill/WRITING-STYLE.md +62 -0
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+ # envelope-post-attempt.sh — built-in post_attempt default (PRD #207, issue
3
+ # #212; renamed from post_worker in #226).
4
+ #
5
+ # Reconciles the iteration's intermediate envelope state on the AFK state
6
+ # file as soon as the runner returns — before any user `post_attempt` hook
7
+ # runs, so a user notifier/pager integration that reads state already sees
8
+ # the worker's terminal result classified.
9
+ #
10
+ # Contract:
11
+ # stdin — JSON object: post_attempt context with `.result.status` of
12
+ # "success" or "fail".
13
+ # stdout — empty (context unchanged). Pure side-effect on the state file.
14
+ # exit — 0. Never aborts the chain.
15
+ #
16
+ # Env contract (set by afk.sh before dispatch):
17
+ # RED_AFK_STATE_FILE — path to the iteration's afk.state.json;
18
+ # when unset/missing, the default is a no-op.
19
+ # RED_AFK_RESULT_STATUS — redundant fallback for tests / runners that
20
+ # can't shape a ctx; if ctx.result.status is
21
+ # absent the env var is used instead.
22
+ #
23
+ # Disable with `afk.hooks.defaults.envelope: false` in `.red/config.yaml`.
24
+
25
+ set -uo pipefail
26
+
27
+ ctx="$(timeout "${RED_SKILLS_HOOK_STDIN_TIMEOUT_S:-5s}" cat 2>/dev/null || true)"
28
+
29
+ state_file="${RED_AFK_STATE_FILE:-}"
30
+ [[ -z "$state_file" || ! -f "$state_file" ]] && exit 0
31
+
32
+ status=""
33
+ if [[ -n "$ctx" ]]; then
34
+ status="$(printf '%s' "$ctx" | jq -r '.result.status // empty' 2>/dev/null || true)"
35
+ fi
36
+ [[ -z "$status" ]] && status="${RED_AFK_RESULT_STATUS:-}"
37
+ [[ -z "$status" ]] && exit 0
38
+
39
+ tmp="${state_file}.tmp.$$"
40
+ if jq --arg s "$status" '.current = ((.current // {}) + {result_status: $s})' \
41
+ "$state_file" > "$tmp" 2>/dev/null; then
42
+ mv "$tmp" "$state_file"
43
+ else
44
+ rm -f "$tmp"
45
+ fi
46
+
47
+ exit 0
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+ # gradle-pre-worktree.sh — built-in pre_worktree default for Gradle projects.
3
+ #
4
+ # Contract (PRD #207, issue #211):
5
+ # stdin — JSON object: pre_worktree context (issue, target, branch, env).
6
+ # stdout — empty (context unchanged) when no build.gradle* is present at
7
+ # $PROJECT_ROOT, OR when RED_AFK_GRADLE_USER_HOME_BASE is unset
8
+ # (opt-in by env var — we do not claim a path on the user's
9
+ # filesystem without their consent). Otherwise a JSON object
10
+ # that merges {env: {GRADLE_USER_HOME: <slot dir>}} into ctx.
11
+ # exit — 0 on either branch. Never aborts the chain.
12
+ #
13
+ # Side effect: mkdir -p the slot Gradle home so daemons / caches are isolated
14
+ # between worker slots.
15
+ #
16
+ # Disable with `afk.hooks.defaults.gradle: false` in `.red/config.yaml`.
17
+
18
+ set -uo pipefail
19
+
20
+ project_root="${PROJECT_ROOT:-$(pwd)}"
21
+ ctx="$(timeout "${RED_SKILLS_HOOK_STDIN_TIMEOUT_S:-5s}" cat 2>/dev/null || true)"
22
+ [[ -z "$ctx" ]] && ctx='{}'
23
+
24
+ shopt -s nullglob
25
+ matches=("$project_root"/build.gradle*)
26
+ shopt -u nullglob
27
+ if [[ "${#matches[@]}" -eq 0 ]]; then
28
+ exit 0
29
+ fi
30
+
31
+ if [[ -z "${RED_AFK_GRADLE_USER_HOME_BASE:-}" ]]; then
32
+ exit 0
33
+ fi
34
+
35
+ slot="${RED_AFK_SLOT:-0}"
36
+ home_dir="${RED_AFK_GRADLE_USER_HOME_BASE}/slot-${slot}"
37
+
38
+ mkdir -p "$home_dir" 2>/dev/null || true
39
+
40
+ printf '%s' "$ctx" | jq -c \
41
+ --arg h "$home_dir" \
42
+ '. + {env: ((.env // {}) + {GRADLE_USER_HOME: $h})}'
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env bash
2
+ # heartbeat-post-attempt.sh — built-in post_attempt default (PRD #207, issue
3
+ # #212; renamed from post_worker in #226).
4
+ #
5
+ # Contract:
6
+ # stdin — JSON object: post_attempt context (issue, workspace, result).
7
+ # stdout — empty (context unchanged). The default is a pure side-effect:
8
+ # it stops the orchestrator's per-minute heartbeat side-channel
9
+ # and writes the "iteration stopped" boundary marker into the
10
+ # iteration log, migrating the inline `heartbeat_stop` that
11
+ # used to fire from afk.sh right after `run_inner` returned.
12
+ # exit — 0. Never aborts the post_attempt chain (the dispatcher already
13
+ # routes post_attempt rc through `continue` policy, but the
14
+ # default also belt-and-braces this with a 0 exit).
15
+ #
16
+ # Env contract (set by afk.sh before dispatch):
17
+ # RED_AFK_HEARTBEAT_PID — pid of the orchestrator heartbeat sub-shell;
18
+ # empty when no heartbeat was started for this
19
+ # iteration (smoke paths). Killing a stale pid
20
+ # is a no-op via `kill -0` guard.
21
+ # RED_AFK_ITER_LOG — path to the iteration log; the boundary line
22
+ # is appended only when the file exists.
23
+ #
24
+ # Disable with `afk.hooks.defaults.heartbeat: false` in `.red/config.yaml`.
25
+
26
+ set -uo pipefail
27
+
28
+ # Drain stdin (context flows through unchanged — emit nothing).
29
+ timeout "${RED_SKILLS_HOOK_STDIN_TIMEOUT_S:-5s}" cat >/dev/null 2>&1 || true
30
+
31
+ pid="${RED_AFK_HEARTBEAT_PID:-}"
32
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
33
+ kill "$pid" 2>/dev/null || true
34
+ # Best-effort reap; the sub-shell may already be exiting.
35
+ wait "$pid" 2>/dev/null || true
36
+ fi
37
+
38
+ iterlog="${RED_AFK_ITER_LOG:-}"
39
+ if [[ -n "$iterlog" && -f "$iterlog" ]]; then
40
+ printf '[heartbeat] iteration stopped at %s\n' "$(date -Is)" >> "$iterlog"
41
+ fi
42
+
43
+ exit 0
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ # validation-post-merge.sh — built-in post_merge default (PRD #207, issue #213).
3
+ #
4
+ # Runs CI/smoke validation against the merged primary checkout so user
5
+ # post_merge hooks see the validation status already reconciled into the
6
+ # context. Migrated from the inline `feedback`-style call: the pre-merge
7
+ # `feedback()` in afk.sh stays as the mechanism-owned safety gate (ADR 0008
8
+ # — anything that must abort the merge loudly is mechanism, not a hook), and
9
+ # this default exposes a post-merge re-run hook authors can disable, observe,
10
+ # or replace.
11
+ #
12
+ # Contract:
13
+ # stdin — JSON object: post_merge context (issue, workspace, merge_commit).
14
+ # stdout — JSON object: the same context with
15
+ # `.result = {validation_status: "passed"|"failed"|"skipped",
16
+ # validation_summary: <one-line string>}` merged into
17
+ # it. Non-JSON stdin → pass-through with no validation attempt.
18
+ # exit — 0. Post_merge policy is `continue`; even a failing validation
19
+ # only logs (it cannot roll back the merge — that ship has sailed
20
+ # at this lifecycle point).
21
+ #
22
+ # Env contract (set by afk.sh before dispatch):
23
+ # RED_AFK_WORKSPACE — primary checkout (post-merge tip). When unset or
24
+ # not a directory, the default is a no-op.
25
+ # RED_AFK_MERGE_COMMIT — full sha of the merge commit on `target`. Not
26
+ # consumed by this default but documented so user
27
+ # hooks reading env directly see a consistent view.
28
+ #
29
+ # Disable with `afk.hooks.defaults.validation: false` in `.red/config.yaml`.
30
+
31
+ set -uo pipefail
32
+
33
+ ctx="$(timeout "${RED_SKILLS_HOOK_STDIN_TIMEOUT_S:-5s}" cat 2>/dev/null || true)"
34
+ ws="${RED_AFK_WORKSPACE:-}"
35
+
36
+ emit_ctx() {
37
+ local status="$1" summary="$2"
38
+ if [[ -z "$ctx" ]]; then
39
+ return 0
40
+ fi
41
+ printf '%s' "$ctx" \
42
+ | jq --arg s "$status" --arg sum "$summary" \
43
+ '.result = ((.result // {}) + {validation_status: $s, validation_summary: $sum})' \
44
+ 2>/dev/null \
45
+ || printf '%s' "$ctx"
46
+ }
47
+
48
+ if [[ -z "$ws" || ! -d "$ws" ]]; then
49
+ emit_ctx "skipped" "no workspace"
50
+ exit 0
51
+ fi
52
+
53
+ if ! [[ -f "$ws/package.json" ]]; then
54
+ emit_ctx "skipped" "no package.json"
55
+ exit 0
56
+ fi
57
+
58
+ # Run pnpm test/typecheck/lint/build at the workspace root if the script
59
+ # exists. This mirrors the mechanism-owned `feedback()` step shape but
60
+ # operates on the merged tip rather than the worker's worktree.
61
+ overall="passed"
62
+ parts=""
63
+ for script in test typecheck lint build; do
64
+ if jq -e --arg s "$script" '.scripts[$s] // empty' "$ws/package.json" >/dev/null 2>&1; then
65
+ if ( cd "$ws" && pnpm -s "$script" >/dev/null 2>&1 ); then
66
+ parts+="${script}:✓ "
67
+ else
68
+ parts+="${script}:✗ "
69
+ overall="failed"
70
+ fi
71
+ else
72
+ parts+="${script}:skip "
73
+ fi
74
+ done
75
+
76
+ emit_ctx "$overall" "${parts% }"
77
+ exit 0
@@ -0,0 +1,72 @@
1
+ # afk detectors
2
+
3
+ Shipped detectors invoked by the AFK runtime's hook orchestrator
4
+ at the `pre-spawn` hook point. Each
5
+ detector is a self-contained shell script that decides, on its own,
6
+ whether it applies to the current project; if so, it writes
7
+ `KEY=value` exports into `$RED_AFK_HOOK_ENV_FILE` and exits 0. The
8
+ orchestrator sources that env-file back into the spawn shell, so a
9
+ detector's exports propagate to the worker.
10
+
11
+ ## Convention
12
+
13
+ A detector is a single `*.sh` file in this directory. Project-local
14
+ detectors at `<project>/.red/hooks/detectors/*.sh` follow the same
15
+ convention. Each script must:
16
+
17
+ 1. Run an **applicability check** first. If the detector does not
18
+ apply to the current project (or required opt-in env vars are
19
+ unset), `exit 1` immediately. Exit code 1 means "not applicable"
20
+ to the orchestrator — it is never an error and never appears in
21
+ the boot-log.
22
+ 2. If applicable, write zero-or-more `KEY=value` lines to
23
+ `$RED_AFK_HOOK_ENV_FILE`, then `exit 0`. The env-file is unique per
24
+ invocation and the orchestrator deletes it after sourcing.
25
+ 3. Any other exit code is treated as an error. On a `pre-*` hook the
26
+ orchestrator aborts; on a `post-*` hook it logs and continues.
27
+
28
+ The orchestrator runs detectors in C-locale alphabetical order:
29
+ shipped first, then project. Detectors share no state — each runs in
30
+ its own subprocess with a fresh `$RED_AFK_HOOK_ENV_FILE`.
31
+
32
+ ## Available env
33
+
34
+ The orchestrator exports the standard AFK contract before invoking a
35
+ detector:
36
+
37
+ - `RED_AFK_SLOT` — supervisor slot index (defaults to `0` when not run
38
+ under the supervisor).
39
+ - `RED_AFK_PLUGIN_DIR`, `PROJECT_ROOT` — discovery roots.
40
+ - `RED_AFK_HOOK_ENV_FILE` — the path the detector must write to.
41
+
42
+ ## Disabling a shipped detector
43
+
44
+ Set the matching key under `afk.hooks.defaults` in `.red/config.yaml`
45
+ to `false`:
46
+
47
+ ```yaml
48
+ afk:
49
+ hooks:
50
+ defaults:
51
+ cargo: false
52
+ ```
53
+
54
+ Project detectors are never gated by config — they only exist when
55
+ the project authored the file.
56
+
57
+ ## Shipped detectors
58
+
59
+ - **`cargo.sh`** — applies on Rust projects (`Cargo.toml` present).
60
+ Exports `CARGO_TARGET_DIR=${RED_AFK_CARGO_TARGET_BASE:-/opt/cargo-target}/slot-${RED_AFK_SLOT}`,
61
+ pre-creating the directory with `mkdir -p` so the first run on a
62
+ fresh host succeeds. Override the base with `RED_AFK_CARGO_TARGET_BASE`.
63
+ - **`gradle.sh`** — applies on Gradle projects (`build.gradle*`
64
+ present) **and** only when `RED_AFK_GRADLE_USER_HOME_BASE` is set in the
65
+ supervisor's env. Without that base var the detector is a no-op
66
+ (exits 1) — opt-in so we never claim a path on the user's
67
+ filesystem without consent. When both conditions hold, exports
68
+ `GRADLE_USER_HOME=${RED_AFK_GRADLE_USER_HOME_BASE}/slot-${RED_AFK_SLOT}`,
69
+ pre-creating the directory.
70
+
71
+ Both detectors give each worker slot its own build cache so Cargo
72
+ and Gradle never serialize on shared lock files.
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ # cargo.sh — afk pre-spawn detector for Rust projects.
3
+ #
4
+ # Applies when a Cargo.toml exists at PROJECT_ROOT. Exports
5
+ # CARGO_TARGET_DIR=${RED_AFK_CARGO_TARGET_BASE:-/opt/cargo-target}/slot-${RED_AFK_SLOT}
6
+ # so each worker slot gets its own target directory and Cargo
7
+ # never serializes on .cargo-lock between workers.
8
+ #
9
+ # Exit codes:
10
+ # 1 — not a Rust project (no Cargo.toml). The orchestrator treats
11
+ # this as "skip silently".
12
+ # 0 — applies; KEY=value line(s) written to $RED_AFK_HOOK_ENV_FILE.
13
+
14
+ set -u
15
+
16
+ project_root="${PROJECT_ROOT:-$(pwd)}"
17
+ [ -f "$project_root/Cargo.toml" ] || exit 1
18
+
19
+ slot="${RED_AFK_SLOT:-0}"
20
+ base="${RED_AFK_CARGO_TARGET_BASE:-/opt/cargo-target}"
21
+ target_dir="${base}/slot-${slot}"
22
+
23
+ mkdir -p "$target_dir"
24
+
25
+ : "${RED_AFK_HOOK_ENV_FILE:?RED_AFK_HOOK_ENV_FILE not set}"
26
+ printf 'CARGO_TARGET_DIR=%s\n' "$target_dir" >> "$RED_AFK_HOOK_ENV_FILE"
27
+ exit 0
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # gradle.sh — afk pre-spawn detector for Gradle projects.
3
+ #
4
+ # Applies when a build.gradle* file exists at PROJECT_ROOT AND the
5
+ # operator has opted in by setting RED_AFK_GRADLE_USER_HOME_BASE. Exports
6
+ # GRADLE_USER_HOME=${RED_AFK_GRADLE_USER_HOME_BASE}/slot-${RED_AFK_SLOT} so each
7
+ # worker slot gets its own Gradle home (caches, daemons, lockfiles).
8
+ #
9
+ # Opt-in by env var is deliberate: we will not claim a path on the
10
+ # user's filesystem without their consent. Without
11
+ # RED_AFK_GRADLE_USER_HOME_BASE the detector is a no-op (exit 1).
12
+ #
13
+ # Exit codes:
14
+ # 1 — not a Gradle project, or RED_AFK_GRADLE_USER_HOME_BASE unset.
15
+ # 0 — applies; KEY=value line written to $RED_AFK_HOOK_ENV_FILE.
16
+
17
+ set -u
18
+
19
+ project_root="${PROJECT_ROOT:-$(pwd)}"
20
+
21
+ shopt -s nullglob
22
+ matches=("$project_root"/build.gradle*)
23
+ shopt -u nullglob
24
+ [ "${#matches[@]}" -gt 0 ] || exit 1
25
+
26
+ [ -n "${RED_AFK_GRADLE_USER_HOME_BASE:-}" ] || exit 1
27
+
28
+ slot="${RED_AFK_SLOT:-0}"
29
+ home_dir="${RED_AFK_GRADLE_USER_HOME_BASE}/slot-${slot}"
30
+
31
+ mkdir -p "$home_dir"
32
+
33
+ : "${RED_AFK_HOOK_ENV_FILE:?RED_AFK_HOOK_ENV_FILE not set}"
34
+ printf 'GRADLE_USER_HOME=%s\n' "$home_dir" >> "$RED_AFK_HOOK_ENV_FILE"
35
+ exit 0
@@ -0,0 +1,81 @@
1
+ # AFK boot-time sweeps (reference)
2
+
3
+ > Extracted from `afk/SKILL.md` for progressive disclosure. Consulted on demand — not the agent's step-by-step loop.
4
+ >
5
+ > How the bundle reclaims stale tmp-lane state at boot. Durable AFK process state
6
+ > lives in the state tier under `.red/state/castle/`; the agent never runs these by
7
+ > hand because they are the bundle's startup hygiene. Referenced from *Bootstrap*
8
+ > and the *Per-Issue Loop* close step.
9
+
10
+ The lane janitor only acts on registered disposable lanes under `.red/tmp/`.
11
+ Tmp lane cleanup never deletes `.red/state/`, plugin stores, tracked config, or
12
+ `.red/researches/`.
13
+
14
+ ## Orphan Cleanup (boot-time)
15
+
16
+ Right after bootstrap and before *Straggler Check*, `/afk` runs two passes. First it reconciles leftover **legacy flat** `.red/tmp/work-*` dirs from the pre-nested scheme (the drain-first cutover, issue #252). This sweep is pid-guarded and slug-sparing: slug-named manual worktrees are left alone, and numeric legacy work dirs are removed only when their recorded or inferable process is no longer live. Then it sweeps the nested attempt dirs in every worker namespace (`.red/tmp/workers/*/*/`, `.red/tmp/go-workers/*/*/`, and `.red/tmp/scout-workers/*/*/`) whose parent worker's `worker.pid` is dead, and afterwards removes dead empty worker shells across those namespaces: dead/corrupt/missing `worker.pid` files plus the worker dir when it contains no attempt dirs or only empty attempt dirs. Live `worker.pid` dirs and non-empty preserved attempt dirs are left untouched. For each orphaned attempt dir:
17
+
18
+ 1. **(Slice D — heartbeat sub-shell retired.)** No zombie reap step is needed; older state files may still carry a `heartbeat_pid` but it's vestigial and ignored.
19
+ 2. **Decide fate from issue state.** `gh issue view N --json labels,state`:
20
+ - `state == CLOSED` → `rm -rf`. Work landed; nothing to inspect.
21
+ - label `ready-for-human` → **split TTL** based on `envelope.posted` in the attempt state file (see *Terminal-Event Envelope* below):
22
+ - `envelope.posted == true` → 1-day TTL. The issue thread already carries the canonical record; the local dir is pure redundancy.
23
+ - `envelope.posted == false` or field missing → 7-day TTL. The envelope POST failed (or this dir predates the envelope writer), so the local notes/log are the only copy.
24
+ - label `running` (orchestrator crashed mid-issue) → restore `ready-for-agent`, post a recovery comment, then `rm -rf`. Leaving the issue eternally `running` is worse than losing the dir.
25
+ - any other state → `rm -rf`.
26
+ 3. **Fallback on gh failure.** Network / rate-limit error → fall back to mtime TTL: 7 days for dirs with a state file, 1 day for dirs without one. Conservative enough to survive transient outages without losing artefacts the human wanted.
27
+
28
+ This removes the manual "remember to clean `.red/tmp/`" discipline. Blocker dirs persist until their TTL expires; everything else self-collects on the next `/afk` run.
29
+
30
+ ## Attempt Cap (boot-time, issue #257)
31
+
32
+ The *Completion sweep* (close step 11) only fires when an issue completes. Issues that **never** complete — blocked-forever work that accumulates retries — would otherwise leak attempt dirs indefinitely. Right after *Orphan Cleanup*, `cap_issue_attempts` walks every attempt dir across all workers, groups them by issue, and per issue prunes (newest attempt kept first) anything over either cap:
33
+
34
+ - **Age cap** — fixed at 14 days. An attempt dir whose mtime is older than this is reclaimed.
35
+ - **Count cap** — fixed at 5. Only the newest five attempts (by attempt number) for one issue are retained; older ones are reclaimed.
36
+
37
+ Both caps share the completion sweep's invariant: a **live** worker's active attempt (state file carrying a live `pid`) is never counted toward the cap nor removed.
38
+
39
+ ## On-Demand Branch Reaper (issue #275)
40
+
41
+ Run `/afk reap` (the bundle's `reap` command) to perform branch hygiene without starting a worker, claiming an issue, or firing lifecycle hooks. The command first prints one line:
42
+
43
+ ```text
44
+ afk branch counts: remote-afk=N local-afk=N
45
+ ```
46
+
47
+ It then applies the same live-branch reapers used during `/afk` boot: remote `afk/*` and local `afk/*`. Open issues and transiently unclassified issues are kept; local branches checked out by any worktree are kept. Each successful deletion logs the branch, issue number, and classification reason. Re-running is a natural no-op once stale refs are gone.
48
+
49
+ ## Docs Sweep (boot-time)
50
+
51
+ After branch cleanup and before the dependency Unblock Sweep, `/afk` verifies
52
+ that source-of-truth docs under `.red/` are visible from the freshly fetched
53
+ base. The sweep covers glossary docs (`.red/CONTEXT.md`,
54
+ `.red/CONTEXT-MAP.md`, `.red/contexts/**`) and ADR docs (`.red/adr/**`) from
55
+ three signals:
56
+
57
+ - working-tree changes and deletions;
58
+ - untracked or ignored docs, including adopter repos that ignore `.red/`;
59
+ - doc paths changed by commits ahead of `origin/<base>`.
60
+
61
+ Operational surfaces are excluded: `.red/tmp/`, `.red/wiki/`, `.red/memory/`,
62
+ `.red/brain/`, and `.red/state/`.
63
+
64
+ When every stranded doc is publishable, `/afk` creates one isolated worktree
65
+ under `.red/tmp/docs-sweep/` from `origin/<base>`, copies only those doc paths,
66
+ creates one `docs:` commit, pushes a branch, opens one PR, merges it, and fetches
67
+ the base afterward. It never commits in the primary checkout, switches branches,
68
+ stashes, or resets.
69
+
70
+ When the base cannot be fetched, the PR lane fails, or an ignored doc path class
71
+ has no tracked precedent on `origin/<base>`, boot halts before issue selection or
72
+ fleet slot spawn. The halt message carries the explicit relative file list so a
73
+ maintainer can land or remove the stranded docs deliberately.
74
+
75
+ ## Fleet mode: the supervisor owns the boot (issue #623)
76
+
77
+ The sweeps above (*Orphan Cleanup*, *Attempt Cap*, live branch cleanup, *Docs Sweep*, the *Unblock Sweep*, and the *Straggler Check*) all read and mutate **shared** `.red/tmp` / branch / `gh` state. When several workers boot at once they would race over that state — the observed failure mode was a fleet collapsing to a single live worker because peers fast-died in their boot sweeps.
78
+
79
+ So in **fleet mode** the **supervisor** runs the full sweep suite **exactly once, before it spawns any worker**, and logs the result to `.red/tmp/supervisors/default/supervisor.log.toonl` (`boot sweeps complete: orphans … | attempt-cap … | branches … | docs-sweep … | unblocked … | stragglers …`). Every worker the supervisor spawns carries a `RED_AFK_SWEEPS_DONE=1` marker; a marked worker's boot is **bootstrap + claim only** — it ensures its dirs/gitignore/`worker.pid` and goes straight to claiming an issue, skipping every shared sweep. This makes respawns cheap and keeps workers from racing each other over boot state. The supervisor runs the sweeps a single time per lifetime: a worker that exits and is respawned does **not** re-trigger them.
80
+
81
+ A **solo** `/afk run` (no supervisor, no marker) is unchanged — it runs the full sweep suite itself, exactly as described above. `/afk run --boot-only` reports which path it took: `sweeps ran` for a solo boot, `sweeps skipped (supervisor-owned)` for a marked worker.