The previous commit introduced a counter leak in the backlog scan path:
handle_ci_exhaustion (without check_only) atomically incremented the CI
fix counter before the WAITING_PRS guard, so an exit 0 that never spawned
a dev-agent would silently consume one of the three allowed fix attempts.
Restore the READY_PR_FOR_INCREMENT / deferred-increment mechanism:
- Backlog scan calls handle_ci_exhaustion with "check_only" (read-only,
no increment) to detect exhaustion without touching the counter.
- The counter is bumped atomically at LAUNCH time via handle_ci_exhaustion
(without check_only), so the increment only happens when we are certain
a dev-agent is being spawned. If a concurrent poller already exhausted
the counter between scan and launch, the LAUNCH call returns 0 and we
bail out cleanly without double-spawning.
The in-progress, stuck-PR, and try_merge_or_rebase paths are unaffected:
they call handle_ci_exhaustion without check_only, which continues to use
the atomic ci_fix_check_and_increment to prevent concurrent double-spawning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add ci_fix_check_and_increment() that performs read + threshold-check +
conditional increment in a single flock-protected Python call, replacing
the prior three-step sequence (ci_fix_count / bash check / ci_fix_increment)
that allowed two concurrent poll invocations to both pass the threshold and
spawn duplicate dev-agents for the same PR.
handle_ci_exhaustion now calls ci_fix_check_and_increment atomically and
returns the new count in CI_FIX_ATTEMPTS; all separate ci_fix_increment
calls after handle_ci_exhaustion (including the deferred READY_PR_FOR_INCREMENT
mechanism) are removed. Log messages updated from CI_FIX_ATTEMPTS+1 to
CI_FIX_ATTEMPTS to reflect the post-increment count.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove || break from the codeberg_api call in the pagination loop.
With set -euo pipefail in all callers, a failed fetch now exits the
function non-zero — matching the original curl -sf behaviour where a
network or auth error aborted the script rather than returning empty
results and risking a duplicate review.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace ${CODEBERG_WEB} with inline https://codeberg.org/${CODEBERG_REPO}
to avoid unbound variable crash in gardener-poll.sh (set -euo pipefail)
- Change sub-issue title prefix from fix: to chore: since it's an
investigation task, not a code fix
- Add emoji prefix to idle_timeout matrix notification for consistency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap ci_fix_count(), ci_fix_increment(), and ci_fix_reset() with flock
on a shared lockfile to prevent concurrent modification of the JSON
tracker. Uses flock(1) in command-wrapping mode so each Python process
holds an exclusive lock for the duration of its read-modify-write cycle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Restore `-d` flag on codeberg_api POST /issues call (regression fix)
- Do NOT apply `formula` label — dev-agent rejects it, blocking the pipeline
- Keep YAML front matter in body only (structural, harmless to freeform processing)
- Quote YAML var values with @json to handle special characters
- Validate formula name against on-disk formulas/*.toml catalog
- Fall back to freeform if Claude hallucinates a formula name
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Planner: both phases use claude -p (one-shot), not interactive
- Vault: document auto-approve/auto-reject paths, not just human escalation
- CHECK_INFRA_RETRY: env var only, not a TOML toggle — separated from TOML keys
- underspecified label: also set by dev-agent.sh mid-run, not just dev-poll
- ci-helpers.sh: add missing review-poll.sh to sourced-by list
- parse-deps.sh: note it is executed via bash, not sourced
- vault: add PROMPT.md to key files list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract CI-exhaustion check/escalate logic into handle_ci_exhaustion() helper.
All three call sites (orphaned PRs, stuck PRs, backlog PRs) now use the shared
function, eliminating future drift between the copies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bug: chicken-egg-ci create-per-file-issues was aliased to shellcheck-only
function. Added generic playbook_lint_per_file() that handles any linter
output format. Renamed action to lint-per-file.
- Bug: cascade-rebase fired retry-merge synchronously after async rebase.
Removed retry-merge and re-approve from recipe — rebase settles, CI reruns,
normal flow handles merge on subsequent cycle.
- Warning: jq calls on PR data lacked || true under set -euo pipefail. Fixed.
- Warning: playbook_rebase_pr and playbook_retrigger_ci incremented
_PB_SUB_CREATED before confirming API success. Now check HTTP status code.
- Warning: Python import tomllib fails on < 3.11. Added try/except fallback
to tomli package.
- Nit: failures_on_unchanged regex broadened to handle generic linter formats
(file.sh:line:col patterns in addition to ShellCheck's "In file line N:").
- Info: match_recipe now logs Python stderr on error instead of silently
falling back to generic recipe.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- dev-agent.sh: add explicit guard that skips formula-labeled issues with a
clear log message instead of silently producing no formula behavior
- BOOTSTRAP.md: rewrite formula label entry to state it is not yet functional
and that dev-agent will skip such issues until feat/formula is merged
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix SC2164: add || exit 1 to bare cd in update-prompt.sh
- Fix SC2155: separate declare and assign in env.sh, supervisor-poll.sh, dev-agent.sh
- Fix SC2034: inline suppression for vars used by sourced helpers
- Remove unused `mergeable` declaration, rename unused loop var to `_w`
- Remove || true from shellcheck CI step — failures are now blocking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add dedup guard: skip dust entries for issues already in dust.jsonl
- Inject already-staged issue list into LLM prompt to prevent re-emission
- Guard mv after jq: only overwrite dust.jsonl if jq succeeded
- Use sort -nu for numeric dedup of issue numbers
- Compute bundle count from distinct issues, not raw entries
- Add 30-day TTL expiry for sub-threshold dust groups
- Fix inconsistent heading levels in bundle body (all ###)
- Add scope note to PROMPT.md (human docs only, not injected)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add dust/ore rule to gardener LLM prompt: trivial tech-debt (comment
fix, rename, style-only, single-line) outputs DUST: JSON instead of
promoting individually
- Parse DUST lines from LLM output, validate JSON, append to dust.jsonl
with timestamp
- After evaluation pass: check groups with 3+ items, create bundled
backlog issue, close source issues with cross-reference
- Add gardener/dust.jsonl to .gitignore
- Create gardener/PROMPT.md documenting the dust vs ore philosophy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>