feat(claude): env-vars refactor + stale-claim cron (Phase 2 final) #610
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#610
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/claude-envvars-and-stale-cron"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Финализация Phase 2 — закрывает #11 (env vars refactor) + #12 (stale-claim cron) одним PR.
#11: env vars вместо файлов
Slash-commands и
_autonomous_pickup.mdтеперь читают persistent User-scope env vars напрямую ([System.Environment]::GetEnvironmentVariable("FORGEJO_TOKEN_BACKEND", "User")) — никакихGet-Content ~/.claude/secrets/*.token.Setup:
scripts/setup-bot-env.ps1(user заменяет placeholders из vault, запускает один раз). После этого env vars в registry, доступны во всех новых shell.#12: stale-claim cron
scripts/cleanup-stale-claims.sh— bash + curl + Python для JSON.forgejo/workflows/stale-claims.yml— cron*/30 * * * *Освобождает issues застрявшие в
status/wip>4h — clear assignees, +status/ready, post explanation comment.Что нужно ПОСЛЕ merge
FORGEJO_BOT_QA_TOKENв Forgejo Actions UI (git.gendsgn.ru/lekss361/gendesign settings → actions → secrets) — bot-qa PATworkflow_dispatchTest plan
claudewindow →/work-as-backend→ pre-flight использует env varssetup-bot-env.ps1— placeholders detected при пустых tokensRefs
runbooks/multi_agent_autonomous_workflow.mdmeta/00_credentials.md(bot PATs)Deep Code Review — verdict
Summary
.claude/agents/**(1 file) +.claude/commands/**(5 files). Human merge required — reviewer bot НЕ мержит per merge policy override.🔴 Critical (BLOCK)
1.
scripts/cleanup-stale-claims.sh:355—CUTOFFnever exported → Python reads"0"→ cron never releases anythingCUTOFFis set as a shell var, не exported. Python heredoc child process не наследует.os.environ.get("CUTOFF", "0")всегда вернёт"0", поэтомуupdated_epoch >= cutoffвсегда true → каждый issue считается fresh → нулевое действие.Fix:
Ilk:
FORGEJO_URL/FORGEJO_REPO/FORGEJO_TOKENприходят из workflowenv:(уже exported в job env) — эти OK. Но shell-levelCUTOFFнужен explicitexport.2.
scripts/cleanup-stale-claims.sh:383—datetime.UTCтребует Python 3.11+ubuntu-latestсейчас 22.04 LTS с системнымpython3= 3.10.datetime.UTCпоявился в 3.11. На runner упадёт сAttributeError. Используется в двух местах (line 345 вnow_utc_epoch()shell helper, line 383 в Python heredoc).Fix:
datetime.timezone.utc(works in 3.8+).3.
scripts/cleanup-stale-claims.sh:408-419— label race: ready added before wip removedПорядок:
/labelsaddstatus/ready/labels/status%2FwipЕсли step 2 fails, issue имеет оба labels (
status/ready+status/wip). Это нарушает status invariant — workers, проверяющие "is ready and not wip", получат inconsistent state.Fix: invert order. Сначала remove wip, потом add ready. Если ready add fails — issue в neutral state без status, что менее опасно.
🟠 High
4.
cleanup-stale-claims.sh:358— pagination отсутствуетlimit=50&page=1— обрабатывается только первые 50 wip issues. Сейчас issue queue маленький, но cron*/30 * * * *× 50 ceiling = at most 100/h освобождений. Если когда-нибудь будет burst (CI failure → multiple stuck) — silently truncated.Fix: loop по
pageпока ответ не пустой.5.
setup-bot-env.ps1:496— token остаётся в$tokenshashtable в memory + script остаётся on-diskКомментарий говорит "можешь удалить локальную копию после запуска", но это manual step. Лучше:
Invoke-Expressionпосле редактирования, илиRemove-Variable tokens; [GC]::Collect()(минор — но соответствует security best-practice)Read-Host -AsSecureStringили изgpg --decrypt, без edit-in-placeНе блокер для merge, но worth для следующей итерации.
🟡 Medium
6.
cleanup-stale-claims.sh:371—updated_at≠ heartbeatIssue
updated_atобновляется при любом edit (label, comment, assignee). Когда worker claim'ит issue (добавляетstatus/wip+ assignee),updated_at= claim time. Если worker делает task >4h без касаний issue — будет released пока ещё жив. Для текущих small tasks 4h enough, но стоит:7.
cleanup-stale-claims.sh:348-350— dead code:iso_to_epoch()shell helper определён, не используетсяВся iso→epoch конверсия делается inline в Python.
8.
.forgejo/workflows/stale-claims.yml:23— нетconcurrency:groupCron каждые 30 min на shared runner. Если предыдущий run ещё running (5min timeout — unlikely, но возможно при API latency), второй stomp'нет. Добавь:
🟢 Low
9.
cleanup-stale-claims.sh:439—print(f"")→print()(cosmetic).10.
setup-bot-env.ps1:499— последние 4 char токена в console output. Минорный info leak в shell history; приемлемо для UX verification но стоит upgrade на****без suffix или показать только при flag-Verbose.11.
_autonomous_pickup.md:47—Out-Null | if (-not $?)patternGetEnvironmentVariableНЕ throws при missing var — возвращает$null.$?checks last command success, и pipe вOut-Nullвсегда $true. Эта проверка молча проходит даже без env var. Используй:if (-not [System.Environment]::GetEnvironmentVariable(...)) { Write-Error ... }.Cross-file impact
workflow_dispatchв Forgejo UI после фиксов 1+2, проверить:meta/00_credentials.mdв vault — должен документироватьFORGEJO_TOKEN_<ROLE>env var naming (sync с этим PR).FORGEJO_BOT_QA_TOKENsecret в Forgejo Actions UI — указано в test plan как post-merge step. ОК.Positive
setup-bot-env.ps1validationREPLACE_ME_FROM_VAULT→ fail-fast._autonomous_pickup.mdтеперь self-documenting с inline check.timeout-minutes: 5— sensible bound.Recommended next steps
datetime.UTC→datetime.timezone.utc, invert label order (remove wip → add ready).workflow_dispatchпосле fix — verify no traceback на 0 stale, no traceback на ≥1 stale.concurrency:group в workflow.Complexity / blast radius
[Environment]::SetEnvironmentVariable($k, $null, "User").Bot status: changes requested, no merge (self-extending — human approval required + 3 critical bugs).
Fixup pushed:
e4dc866Все 🔴 BLOCK критичные закрыты + большая часть 🟠/🟡/🟢.
🔴 Critical (3/3 ✅)
export CUTOFF+export STALE_HOURS— Python heredoc child process теперь получает реальные values. Plus added abort при CUTOFF=0 для defense-in-depthdatetime.UTC→datetime.timezone.utc(Python 3.8+ compat) — обе occurrences🟠 + 🟡 + 🟢 (5/6 ✅, 1 skipped)
iso_to_epoch()shell helperconcurrency: group: stale-claims, cancel-in-progress: falseв workflowupdated_at != heartbeatassumption в script header_autonomous_pickup.md: fixed brokenOut-Null | if (-not $?)pattern → directif (-not GetEnvironmentVariable(...))checkНе сделано (skipped, low risk)
print(f"")→print()— cosmetic, изменено в fixup автоматически.Self-extending nota bene
Этот PR модифицирует
.claude/agents/**+.claude/commands/**— попадает под self-extending guard. Bot НЕ мержит (per merge policy). Human merge required.Готов к re-review.
Re-Review — verdict (delta
297eb29→e4dc866)Summary
scripts/cleanup-stale-claims.sh,.forgejo/workflows/stale-claims.yml,.claude/agents/_autonomous_pickup.md)e4dc866.claude/agents/_autonomous_pickup.md+ 5x.claude/commands/work-as-*.md).🔴 Critical fixes verified
1.
CUTOFFnot exported → cron no-op — ✅ FIXEDscripts/cleanup-stale-claims.sh:34—export STALE_HOURS="${STALE_HOURS:-4}"scripts/cleanup-stale-claims.sh:50—export CUTOFF=$((NOW - STALE_SECS))cutoff == 0(lines 112-114) — даже если кто-то снова забудетexportв будущем рефакторинге, цикл остановится с error вместо silent no-op.<<'PYEOF'(line 69) — env-var path корректен (Python читает черезos.environ.get, не через shell interpolation).2.
datetime.UTC→ Python 3.10 ImportError — ✅ FIXEDdatetime.timezone.utc:now_utc_epoch()(line 41)now_epoch(line 76)iso_to_epoch()helper (другаяdatetime.UTCточка) удалён целиком — мёртвый код после внутри-Python переноса парсинга.datetime.UTCв head_sha=e4dc866 → 0 occurrences. ubuntu-latest python3.10 safe.3. Label race (add ready перед remove wip) — ✅ FIXED
scripts/cleanup-stale-claims.sh:126-137:status/wipFIRST (line 130)status/ready(line 136)status/*< both-statuses bad state. Корректная trade-off для idempotent retry на следующем cron tick.🟠 / 🟡 / 🟢 follow-ups verified
iso_to_epoch()shell helperconcurrency: group: stale-claims, cancel-in-progress: false(workflow lines 18-20)updated_at != heartbeatassumption undocumented_autonomous_pickup.mdbrokenOut-Null | if (-not $?)patternif (-not GetEnvironmentVariable(...))checkSkipped (acknowledged trade-offs)
print(f"")→print()— author заявил cosmetic; в delta строка 191→192 changed.Pre-flight checks
head.sha = e4dc866e2044ca9520cd0134c60524bf9c178849,mergeable=true,state=openfeat/claude-envvars-and-stale-cronComplexity / blast radius
Recommended next steps
FORGEJO_BOT_QA_TOKENsecret в Forgejo Actions UI (упомянуто в PR body)workflow_dispatchзапуск на пустойstatus/wipqueue — sanity check, что log output корректный + exit 0updated_atproxyPositive
cutoff == 0— pattern, который стоит закрепить (memory:[heredoc-shell-var-not-exported]— guard превращает silent-no-op в loud-fail)🚨 Self-extending — human merge required. Bot НЕ мержит.