Skip to content

fix(release): route Scoop manifest update through a PR instead of a direct push - #226

Merged
getappz merged 1 commit into
masterfrom
fix/scoop-release-pr
Jul 17, 2026
Merged

getappz merged 1 commit into
masterfrom
fix/scoop-release-pr

Conversation

@getappz

@getappz getappz commented Jul 17, 2026 •

Copy link
Copy Markdown
Owner

The `Update Scoop` job in release.yml pushed the manifest straight to `master` using an admin PAT, relying on `enforce_admins` being off. It's now on (`gh api repos/getappz/agentflare/branches/master/protection` confirms it), so both the v1.3.1 and v1.4.0 releases hit `GH006: Protected branch update failed`.

Fix: the job now creates a branch, opens a PR, and auto-merges it (0 required approvals, so it merges as soon as required checks pass) — same end result, but goes through the normal PR flow instead of trying to bypass branch protection. Still uses the owner PAT (`CLA_SIGNATURES_TOKEN`) since `GITHUB_TOKEN`-authored branch pushes don't trigger the required CI checks.

Summary by CodeRabbit

  • Bug Fixes
    • Scoop manifest updates now use a pull request workflow instead of direct commits to the main branch.
    • Releases with no manifest changes now skip unnecessary updates.
    • Manifest update pull requests are automatically merged and their temporary branches removed.

…irect push to master

master's enforce_admins is now on, so the admin PAT push (GH006) fails —
this job route via branch + PR + auto-merge instead, which works
regardless of enforce_admins.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The release workflow now updates the Scoop manifest by creating and automatically merging a pull request from a versioned branch, with expanded pull request permissions and an early exit when no manifest changes are detected.

Changes

Scoop manifest update

Layer / File(s) Summary
Workflow permissions and update entrypoint
.github/workflows/release.yml
The update-scoop job adds pull-requests: write permission, updates checkout comments, and renames the manifest update step.
Branch, pull request, and merge flow
.github/workflows/release.yml
The workflow skips unchanged manifests, creates and pushes a versioned branch, opens a pull request against master, then auto-squash merges it and deletes the branch using the configured token.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant update_scoop as update-scoop job
  participant manifest as bucket/agentflare.json
  participant github as GitHub repository
  update_scoop->>manifest: Check for manifest changes
  update_scoop->>github: Push scoop-manifest-${VERSION} branch
  update_scoop->>github: Create pull request targeting master
  update_scoop->>github: Auto-squash merge and delete branch
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change and rationale, but it omits the template's required Test plan and Notes for reviewers sections. Add the Test plan checklist and Notes for reviewers sections, including risk areas and backward compatibility notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: routing Scoop manifest updates through a PR instead of a direct push.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/scoop-release-pr

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 198-200: Update the release workflow permissions block to grant
the token used by CLA_SIGNATURES_TOKEN pull-request write access, while
retaining contents write access. Ensure the relevant pull-request permission is
configured for the fine-grained PAT documented in the CLA workflow.
- Around line 263-266: Update the release workflow around the gh pr create and
gh pr merge commands to make the approval requirement explicit: either provision
a trusted, distinct approver so auto-merge can complete hands-off, or
remove/defer automatic merging and add a manual approval step before merging the
generated Scoop manifest PR.
- Around line 258-266: Update the Scoop release flow around the branch variable
and gh pr create/merge commands to detect an existing open PR for the
scoop-manifest-${VERSION} branch, reuse its PR number or reference, and skip
creation when found. For a new branch PR, retain the current creation behavior;
in both cases, invoke gh pr merge on the existing PR head with the current auto,
squash, and delete-branch options.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ff7f9da1-8e80-479e-9b88-3e9b1facd2ca

📥 Commits

Reviewing files that changed from the base of the PR and between 981c03b and 46dd2b6.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

Comment on lines +198 to +200
permissions:
contents: write
pull-requests: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
rg -n -C3 'CLA_SIGNATURES_TOKEN|Contents:' \
  .github/workflows/cla.yml .github/workflows/release.yml

Repository: getappz/agentflare

Length of output: 2168


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

sed -n '180,220p' .github/workflows/release.yml
printf '\n----\n'
sed -n '250,275p' .github/workflows/release.yml
printf '\n----\n'
sed -n '30,45p' .github/workflows/cla.yml

Repository: getappz/agentflare

Length of output: 3756


🌐 Web query:

GitHub fine-grained personal access token permissions create pull request pull requests write contents write gh pr create

💡 Result:

To use the GitHub CLI (gh) command gh pr create with a fine-grained personal access token (PAT), you must configure specific repository permissions [1]. For the most basic pull request creation, you require the following minimum permissions: 1. Pull Requests: Read and write [1] 2. Metadata: Read-only (This is automatically added as a mandatory requirement when you select 'Pull Requests: Read and write') [1] While the core functionality for creating a PR is covered by these, users often encounter errors (such as inability to access repository metadata or branch references) if their token lacks broader access [1]. If you encounter errors like "Resource not accessible by personal access token," it is recommended to also include: - Contents: Read-only (or Read and write, depending on your broader workflow needs) [1] Additional notes: - Using 'write' access automatically includes 'read' access for that permission [2]. - If you use features like --project (adding a PR to a project), you will need additional permissions such as Project (read) [3][4]. - The GitHub CLI does not always explicitly inform you which specific fine-grained permission is missing due to API limitations [1]. If a command fails unexpectedly, verify your token's configured permissions in your GitHub settings under Personal Access Tokens [2].

Citations:


Grant CLA_SIGNATURES_TOKEN pull-request access. gh pr create and gh pr merge use GH_TOKEN, so the job’s permissions block only affects GITHUB_TOKEN. The fine-grained PAT documented in .github/workflows/cla.yml needs Pull Requests: write in addition to Contents: write.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 198 - 200, Update the release
workflow permissions block to grant the token used by CLA_SIGNATURES_TOKEN
pull-request write access, while retaining contents write access. Ensure the
relevant pull-request permission is configured for the fine-grained PAT
documented in the CLA workflow.

Comment on lines +258 to +266
BRANCH="scoop-manifest-${VERSION}"
git checkout -b "$BRANCH"
git commit -m "scoop manifest: agentflare ${VERSION}"
git push origin "$BRANCH"

gh pr create --base master --head "$BRANCH" \
--title "scoop manifest: agentflare ${VERSION}" \
--body "Automated Scoop manifest update for release ${GITHUB_REF_NAME}."
gh pr merge "$BRANCH" --auto --squash --delete-branch

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
VERSION="${1:?usage: $0 <version>}"
BRANCH="scoop-manifest-${VERSION}"

git ls-remote --heads origin "refs/heads/${BRANCH}"
gh pr list --repo getappz/agentflare --base master --head "${BRANCH}" \
  --state open --json number,url,headRefOid

Repository: getappz/agentflare

Length of output: 195


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Locate the workflow and inspect the relevant section with line numbers.
git ls-files '.github/workflows/release.yml'
wc -l .github/workflows/release.yml
sed -n '230,290p' .github/workflows/release.yml

Repository: getappz/agentflare

Length of output: 2372


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Inspect the workflow around the manifest publishing steps.
sed -n '1,230p' .github/workflows/release.yml

Repository: getappz/agentflare

Length of output: 8557


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

git init --bare "$tmp/remote.git" >/dev/null
git clone "$tmp/remote.git" "$tmp/a" >/dev/null
cd "$tmp/a"
git config user.name "bot"
git config user.email "bot@example.com"

mkdir -p bucket
echo '{"version":"1.2.3"}' > bucket/agentflare.json
git add bucket/agentflare.json
git commit -m "scoop manifest: agentflare 1.2.3" >/dev/null
git branch -M master
git push origin master:scoop-manifest-1.2.3 >/dev/null

git clone "$tmp/remote.git" "$tmp/b" >/dev/null
cd "$tmp/b"
git config user.name "bot"
git config user.email "bot@example.com"

git checkout -b master origin/master >/dev/null
mkdir -p bucket
echo '{"version":"1.2.3"}' > bucket/agentflare.json
git add bucket/agentflare.json
git commit -m "scoop manifest: agentflare 1.2.3" >/dev/null

set +e
git push origin HEAD:scoop-manifest-1.2.3
code=$?
set -e

echo "exit=$code"

Repository: getappz/agentflare

Length of output: 1122


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

git init --bare "$tmp/remote.git" >/dev/null
git clone "$tmp/remote.git" "$tmp/repo" >/dev/null
cd "$tmp/repo"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]`@users.noreply.github.com`"

# Base branch that mimics the workflow checkout.
echo base > README.md
git add README.md
git commit -m "base" >/dev/null
git branch -M master
git push origin master >/dev/null

make_commit() {
  rm -rf bucket
  mkdir -p bucket
  printf '%s\n' '{"version":"1.2.3","hash":"sha256:abc"}' > bucket/agentflare.json
  git add bucket/agentflare.json
  git commit -m "scoop manifest: agentflare 1.2.3" >/dev/null
  git rev-parse HEAD
}

git checkout -b scoop-manifest-1.2.3 master >/dev/null
sha1="$(make_commit)"
git push origin HEAD:scoop-manifest-1.2.3 >/dev/null

git checkout master >/dev/null
git branch -D scoop-manifest-1.2.3 >/dev/null
git checkout -b scoop-manifest-1.2.3 master >/dev/null
sha2="$(make_commit)"

set +e
git push origin HEAD:scoop-manifest-1.2.3
push_exit=$?
set -e

printf 'sha1=%s\nsha2=%s\npush_exit=%s\n' "$sha1" "$sha2" "$push_exit"

Repository: getappz/agentflare

Length of output: 1098


🌐 Web query:

gh pr create existing branch already has a pull request behavior open source documentation

💡 Result:

When using the GitHub CLI (gh), the gh pr create command is designed to prevent the creation of duplicate pull requests [1][2]. If an open pull request already exists for the same head branch and base branch, the command will fail and output an error message containing the URL of the existing pull request [1][3][4]. Because gh pr create does not automatically "upsert" (update) an existing pull request, users seeking to maintain or update an existing PR typically employ one of the following strategies in their scripts: 1. Manual Update: If a PR already exists, the intended changes are usually pushed via git push (or git push --force if necessary), which automatically updates the open pull request associated with that branch [3][5]. 2. Conditional Logic: Scripts often check for an existing PR before running the create command [3][5]. A common pattern is to attempt to list the PR for the current branch first; if one is found, the script can then use gh pr edit to update metadata (such as the title or body) instead of trying to create a new one [3][5]. The command does not currently provide a specific flag to automatically update an existing PR, nor does it return a unique exit code specifically for an "already exists" state, making it necessary to parse the error output or check for the PR existence programmatically if automated handling is required [3][5].

Citations:


Reuse the existing Scoop PR on reruns. The branch recreation already lands on the same commit, but gh pr create will fail once an open PR exists for scoop-manifest-${VERSION}. Look up that PR and reuse it, then enable merge on the existing head instead of creating a new one.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 258 - 266, Update the Scoop
release flow around the branch variable and gh pr create/merge commands to
detect an existing open PR for the scoop-manifest-${VERSION} branch, reuse its
PR number or reference, and skip creation when found. For a new branch PR,
retain the current creation behavior; in both cases, invoke gh pr merge on the
existing PR head with the current auto, squash, and delete-branch options.

Comment on lines +263 to +266
gh pr create --base master --head "$BRANCH" \
--title "scoop manifest: agentflare ${VERSION}" \
--body "Automated Scoop manifest update for release ${GITHUB_REF_NAME}."
gh pr merge "$BRANCH" --auto --squash --delete-branch

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
gh api repos/getappz/agentflare/branches/master/protection/required_pull_request_reviews \
  --jq '{required_approving_review_count,require_code_owner_reviews,require_last_push_approval}'

Repository: getappz/agentflare

Length of output: 390


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Show the relevant workflow section with line numbers.
sed -n '190,280p' .github/workflows/release.yml | cat -n

# Look for repository-side branch protection / ruleset config.
git ls-files .github | sed -n '1,200p'
rg -n "required_approving_review_count|require_code_owner_reviews|require_last_push_approval|review|auto-merge|gh pr merge|master" .github -S

Repository: getappz/agentflare

Length of output: 5008


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Inspect the exact lines around the PR creation/merge logic.
nl -ba .github/workflows/release.yml | sed -n '200,270p'

# Search for any repo-local branch protection/ruleset config.
git ls-files | rg '(^|/)(branch-protection|ruleset|protection|settings)\.|\.github/.+(rules|protection)|CODEOWNERS$'
rg -n "required_approving_review_count|require_code_owner_reviews|require_last_push_approval|auto-merge|gh pr merge|gh pr create|master" -S .github . 2>/dev/null

Repository: getappz/agentflare

Length of output: 196


gh pr merge --auto still depends on a separate approval. .github/workflows/release.yml already notes that master requires an approved PR plus passing checks, so this PR will remain pending until a distinct reviewer approves it. If this is meant to be hands-off, provision a trusted approver; otherwise make manual approval an explicit release step.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 263 - 266, Update the release
workflow around the gh pr create and gh pr merge commands to make the approval
requirement explicit: either provision a trusted, distinct approver so
auto-merge can complete hands-off, or remove/defer automatic merging and add a
manual approval step before merging the generated Scoop manifest PR.

@getappz
getappz merged commit a0b1907 into master Jul 17, 2026
17 checks passed
@getappz
getappz deleted the fix/scoop-release-pr branch July 17, 2026 17:40
getappz pushed a commit that referenced this pull request Sep 11, 2026
…fmt/LOC gate

cargo fmt --check was flagging the job-failure message line as over
the 100-char width, and work.rs is already pinned at the frozen
2100-line cap, so wrapping it would have failed the LOC gate instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 7bda56eb-8cf5-4c3f-ba3e-b060851a47ec
getappz pushed a commit that referenced this pull request Sep 12, 2026
… items (item #226)

A job queued (or queued-for-retry) against an item survives daemon
restarts on disk in agent_jobs with no state check of its own, and
claim() doesn't guard against reopening a cancelled/completed item
either (intentionally, since a human re-claiming via redispatch is
legitimate). That let a stale automatic retry silently reopen an
already-cancelled item and spawn a fresh session.

Extend the execution-time re-resolve in fresh_dispatch_pair to also
surface the item's current state group, and have WorkItemExecutor
refuse to dispatch (fatal, no retry) when that group is cancelled or
completed. Test moved to a separate include file to stay under the
work.rs LOC gate.

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 093ada26-9f05-466b-99c3-d1bc149ff840
getappz pushed a commit that referenced this pull request Sep 12, 2026
…fmt/LOC gate

cargo fmt --check was flagging the job-failure message line as over
the 100-char width, and work.rs is already pinned at the frozen
2100-line cap, so wrapping it would have failed the LOC gate instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 7bda56eb-8cf5-4c3f-ba3e-b060851a47ec
getappz added a commit that referenced this pull request Sep 12, 2026
…n silently reopen a cancelled item and re-run it (#662)

* fix(dispatcher): refuse stale queued jobs against cancelled/completed items (item #226)

A job queued (or queued-for-retry) against an item survives daemon
restarts on disk in agent_jobs with no state check of its own, and
claim() doesn't guard against reopening a cancelled/completed item
either (intentionally, since a human re-claiming via redispatch is
legitimate). That let a stale automatic retry silently reopen an
already-cancelled item and spawn a fresh session.

Extend the execution-time re-resolve in fresh_dispatch_pair to also
surface the item's current state group, and have WorkItemExecutor
refuse to dispatch (fatal, no retry) when that group is cancelled or
completed. Test moved to a separate include file to stay under the
work.rs LOC gate.

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 093ada26-9f05-466b-99c3-d1bc149ff840

* fix(dispatcher): shorten item #226 state-gate message to satisfy rustfmt/LOC gate

cargo fmt --check was flagging the job-failure message line as over
the 100-char width, and work.rs is already pinned at the frozen
2100-line cap, so wrapping it would have failed the LOC gate instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 7bda56eb-8cf5-4c3f-ba3e-b060851a47ec

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>
getappz added a commit that referenced this pull request Sep 12, 2026
…limit cooldown never fires for autonomous dispatch (#664)

* Both call sites (the two identified in the bug report as the root cause) are fixed, and there are no other callers of `finish_workflow_failed` left unpatched.

Summary of verification:
- Both `finish_workflow_failed` call sites in `execute_workflow` now fold the failing step's `last_error` into the error message, exactly as prescribed.
- `crates/flare-workflow` full test suite passes (I reran it, including the new `workflow_level_error_preserves_the_failed_steps_real_reason` regression test).
- The main `agentflare` binary compiles and its 3 `classify_and_cooldown` tests pass (I ran this myself — the implementer's own session ended without ever confirming this, having only started it as a background task and then stopping without waiting for the result, which is exactly the kind of unverified completion this process asks reviewers to catch).
- Fallback behavior (`state_store.load(...).ok()`) is safe if the store read fails — degrades to the generic message rather than panicking.
- No other `finish_workflow_failed` call sites were missed.

REVIEW_APPROVED

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/229-workflow-level-failure-discards-the-real
Agentflare-Item: 229-workflow-level-failure-discards-the-real

* wip(sdd-loop): task 0 checkpoint (#665)

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/219-borrow-notis-handoff-recursion-guard
Agentflare-Item: 219-borrow-notis-handoff-recursion-guard

Co-authored-by: shiva <shiva@gosysinfo.tech>

* fix(auth): detect and surface expired agent auth instead of silent retries (#666)

Root-cause of item #164's 15h incident: diagnostic_suffix preferred stdout
over stderr, discarding real CLI error text; the only failure classifier
was rate-limiting, so an expired-auth failure fell through to
cooldown-and-retry against a credential that fails identically every time.

- diagnostic_suffix now always includes a non-empty stderr tail alongside
  stdout instead of only falling back to it when stdout is empty
- auth_runner::is_auth_expired + ExitKind::AuthExpired classify
  expired-auth failures distinctly from rate limits; the interactive
  retry loop now prompts for re-auth instead of rotating profiles
- cli::work::handle_auth_expired (split into work_auth_expiry.rs to stay
  under work.rs's frozen LOC limit) intercepts before classify_and_cooldown,
  fails the job immediately (fatal: true, no retry-budget burn), applies
  an idempotent auth-expired label, logs the alert to both stderr and the
  job's own log file, and reuses notify_human_gate for immediate alerting
- work_item_pipeline's sdd_loop map_err sites short-circuit their own
  RetryPolicy via StepResult::Failed instead of a generic Err

Agentflare-Agent: claude-code
Agentflare-Branch: task/173-implement-detect-and-surface-agent-auth
Agentflare-Item: 173
Agentflare-Session: d168de95-65a5-4e8c-b013-83e56df9cc97

Co-authored-by: shiva <shiva@gosysinfo.tech>

* ## Summary (#667)

Implemented identity-scoped hook state for item #220:

- Added `optimize::runtime::scoped_session_key(agent, session_id)` → `"{agent}:{session_id}"`.
- `hook::pre_tool_use` and `hook::prompt_submit` now key `RuntimeState::sessions` and `staleness_checked_sessions` by this scoped key (using the already-normalized `agent` param each already received) instead of the bare session id.
- `hook_completion_gate::post_tool_use` uses the same scoped key for its `sessions` lookups and for `invalidate_verification`/`invalidate_review`.
- `hook::post_tool_failure`'s `nudge_pace` key is now `vent-nudge:{agent}:{topic_key}` instead of `vent-nudge:{topic_key}`.
- Audited the rest of the hook path: `hook_redirect.rs` has no session-keyed state, and `coaching/store.rs`'s nudge_pace keys are rule-id-based, not session-scoped, so out of scope per the task.

Single-agent behavior is unchanged since the scoped key is just `agent:session_id`, and `prune_stale_sessions` naturally prunes stale scoped entries the same way it always did.

**Tests:** `cargo test hook` → 117 passed. `cargo test optimize` → 97 passed, 1 pre-existing unrelated failure (`mcp_prompts::tests::optimize_review_returns_full_skill_body`, a skill-body text assertion in `mcp_prompts.rs` unrelated to hook state) — confirmed by stashing my changes and re-running against unmodified HEAD, same failure. Added `scoped_session_key_differs_by_agent_for_same_session_id` regression test.

**Concern:** this environment's `cargo` (via the configured `sccache` rustc-wrapper + this worktree's on-disk `target/`) fails all builds with a spurious "Read-only file system" error on writing `.d` dep-info files — unrelated to this change, reproducible on a clean build with no edits. I worked around it by building/testing with `--target-dir /tmp/... --config 'build.rustc-wrapper=""'`. Worth flagging separately since it'll block any `cargo build`/`cargo test` in this worktree until fixed.

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/220-borrow-notis-identity-scoped-hook-state
Agentflare-Item: 220-borrow-notis-identity-scoped-hook-state

Co-authored-by: shiva <shiva@gosysinfo.tech>

* wip(sdd-loop): task 0 checkpoint (#668)

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/217-borrow-notis-memory-recall-dedup-cache
Agentflare-Item: 217-borrow-notis-memory-recall-dedup-cache

Co-authored-by: shiva <shiva@gosysinfo.tech>

* chore: item::claim() doesn't check item state — a stale queued job can silently reopen a cancelled item and re-run it (#662)

* fix(dispatcher): refuse stale queued jobs against cancelled/completed items (item #226)

A job queued (or queued-for-retry) against an item survives daemon
restarts on disk in agent_jobs with no state check of its own, and
claim() doesn't guard against reopening a cancelled/completed item
either (intentionally, since a human re-claiming via redispatch is
legitimate). That let a stale automatic retry silently reopen an
already-cancelled item and spawn a fresh session.

Extend the execution-time re-resolve in fresh_dispatch_pair to also
surface the item's current state group, and have WorkItemExecutor
refuse to dispatch (fatal, no retry) when that group is cancelled or
completed. Test moved to a separate include file to stay under the
work.rs LOC gate.

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 093ada26-9f05-466b-99c3-d1bc149ff840

* fix(dispatcher): shorten item #226 state-gate message to satisfy rustfmt/LOC gate

cargo fmt --check was flagging the job-failure message line as over
the 100-char width, and work.rs is already pinned at the frozen
2100-line cap, so wrapping it would have failed the LOC gate instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 7bda56eb-8cf5-4c3f-ba3e-b060851a47ec

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>
getappz added a commit that referenced this pull request Sep 12, 2026
* wip(sdd-loop): task 0 checkpoint

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/218-borrow-notis-pre-handoff-secret-scan
Agentflare-Item: 218-borrow-notis-pre-handoff-secret-scan

* wip(sdd-loop): task 0 checkpoint

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/218-borrow-notis-pre-handoff-secret-scan
Agentflare-Item: 218-borrow-notis-pre-handoff-secret-scan

* chore: item::claim() doesn't check item state — a stale queued job can silently reopen a cancelled item and re-run it (#662)

* fix(dispatcher): refuse stale queued jobs against cancelled/completed items (item #226)

A job queued (or queued-for-retry) against an item survives daemon
restarts on disk in agent_jobs with no state check of its own, and
claim() doesn't guard against reopening a cancelled/completed item
either (intentionally, since a human re-claiming via redispatch is
legitimate). That let a stale automatic retry silently reopen an
already-cancelled item and spawn a fresh session.

Extend the execution-time re-resolve in fresh_dispatch_pair to also
surface the item's current state group, and have WorkItemExecutor
refuse to dispatch (fatal, no retry) when that group is cancelled or
completed. Test moved to a separate include file to stay under the
work.rs LOC gate.

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 093ada26-9f05-466b-99c3-d1bc149ff840

* fix(dispatcher): shorten item #226 state-gate message to satisfy rustfmt/LOC gate

cargo fmt --check was flagging the job-failure message line as over
the 100-char width, and work.rs is already pinned at the frozen
2100-line cap, so wrapping it would have failed the LOC gate instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 7bda56eb-8cf5-4c3f-ba3e-b060851a47ec

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>

* ci: Workflow-level failure discards the real per-step error, so rate-limit cooldown never fires for autonomous dispatch (#664)

* Both call sites (the two identified in the bug report as the root cause) are fixed, and there are no other callers of `finish_workflow_failed` left unpatched.

Summary of verification:
- Both `finish_workflow_failed` call sites in `execute_workflow` now fold the failing step's `last_error` into the error message, exactly as prescribed.
- `crates/flare-workflow` full test suite passes (I reran it, including the new `workflow_level_error_preserves_the_failed_steps_real_reason` regression test).
- The main `agentflare` binary compiles and its 3 `classify_and_cooldown` tests pass (I ran this myself — the implementer's own session ended without ever confirming this, having only started it as a background task and then stopping without waiting for the result, which is exactly the kind of unverified completion this process asks reviewers to catch).
- Fallback behavior (`state_store.load(...).ok()`) is safe if the store read fails — degrades to the generic message rather than panicking.
- No other `finish_workflow_failed` call sites were missed.

REVIEW_APPROVED

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/229-workflow-level-failure-discards-the-real
Agentflare-Item: 229-workflow-level-failure-discards-the-real

* wip(sdd-loop): task 0 checkpoint (#665)

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/219-borrow-notis-handoff-recursion-guard
Agentflare-Item: 219-borrow-notis-handoff-recursion-guard

Co-authored-by: shiva <shiva@gosysinfo.tech>

* fix(auth): detect and surface expired agent auth instead of silent retries (#666)

Root-cause of item #164's 15h incident: diagnostic_suffix preferred stdout
over stderr, discarding real CLI error text; the only failure classifier
was rate-limiting, so an expired-auth failure fell through to
cooldown-and-retry against a credential that fails identically every time.

- diagnostic_suffix now always includes a non-empty stderr tail alongside
  stdout instead of only falling back to it when stdout is empty
- auth_runner::is_auth_expired + ExitKind::AuthExpired classify
  expired-auth failures distinctly from rate limits; the interactive
  retry loop now prompts for re-auth instead of rotating profiles
- cli::work::handle_auth_expired (split into work_auth_expiry.rs to stay
  under work.rs's frozen LOC limit) intercepts before classify_and_cooldown,
  fails the job immediately (fatal: true, no retry-budget burn), applies
  an idempotent auth-expired label, logs the alert to both stderr and the
  job's own log file, and reuses notify_human_gate for immediate alerting
- work_item_pipeline's sdd_loop map_err sites short-circuit their own
  RetryPolicy via StepResult::Failed instead of a generic Err

Agentflare-Agent: claude-code
Agentflare-Branch: task/173-implement-detect-and-surface-agent-auth
Agentflare-Item: 173
Agentflare-Session: d168de95-65a5-4e8c-b013-83e56df9cc97

Co-authored-by: shiva <shiva@gosysinfo.tech>

* ## Summary (#667)

Implemented identity-scoped hook state for item #220:

- Added `optimize::runtime::scoped_session_key(agent, session_id)` → `"{agent}:{session_id}"`.
- `hook::pre_tool_use` and `hook::prompt_submit` now key `RuntimeState::sessions` and `staleness_checked_sessions` by this scoped key (using the already-normalized `agent` param each already received) instead of the bare session id.
- `hook_completion_gate::post_tool_use` uses the same scoped key for its `sessions` lookups and for `invalidate_verification`/`invalidate_review`.
- `hook::post_tool_failure`'s `nudge_pace` key is now `vent-nudge:{agent}:{topic_key}` instead of `vent-nudge:{topic_key}`.
- Audited the rest of the hook path: `hook_redirect.rs` has no session-keyed state, and `coaching/store.rs`'s nudge_pace keys are rule-id-based, not session-scoped, so out of scope per the task.

Single-agent behavior is unchanged since the scoped key is just `agent:session_id`, and `prune_stale_sessions` naturally prunes stale scoped entries the same way it always did.

**Tests:** `cargo test hook` → 117 passed. `cargo test optimize` → 97 passed, 1 pre-existing unrelated failure (`mcp_prompts::tests::optimize_review_returns_full_skill_body`, a skill-body text assertion in `mcp_prompts.rs` unrelated to hook state) — confirmed by stashing my changes and re-running against unmodified HEAD, same failure. Added `scoped_session_key_differs_by_agent_for_same_session_id` regression test.

**Concern:** this environment's `cargo` (via the configured `sccache` rustc-wrapper + this worktree's on-disk `target/`) fails all builds with a spurious "Read-only file system" error on writing `.d` dep-info files — unrelated to this change, reproducible on a clean build with no edits. I worked around it by building/testing with `--target-dir /tmp/... --config 'build.rustc-wrapper=""'`. Worth flagging separately since it'll block any `cargo build`/`cargo test` in this worktree until fixed.

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/220-borrow-notis-identity-scoped-hook-state
Agentflare-Item: 220-borrow-notis-identity-scoped-hook-state

Co-authored-by: shiva <shiva@gosysinfo.tech>

* wip(sdd-loop): task 0 checkpoint (#668)

Agentflare-Agent: claude-code_2-1-268_agent
Agentflare-Branch: task/217-borrow-notis-memory-recall-dedup-cache
Agentflare-Item: 217-borrow-notis-memory-recall-dedup-cache

Co-authored-by: shiva <shiva@gosysinfo.tech>

* chore: item::claim() doesn't check item state — a stale queued job can silently reopen a cancelled item and re-run it (#662)

* fix(dispatcher): refuse stale queued jobs against cancelled/completed items (item #226)

A job queued (or queued-for-retry) against an item survives daemon
restarts on disk in agent_jobs with no state check of its own, and
claim() doesn't guard against reopening a cancelled/completed item
either (intentionally, since a human re-claiming via redispatch is
legitimate). That let a stale automatic retry silently reopen an
already-cancelled item and spawn a fresh session.

Extend the execution-time re-resolve in fresh_dispatch_pair to also
surface the item's current state group, and have WorkItemExecutor
refuse to dispatch (fatal, no retry) when that group is cancelled or
completed. Test moved to a separate include file to stay under the
work.rs LOC gate.

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 093ada26-9f05-466b-99c3-d1bc149ff840

* fix(dispatcher): shorten item #226 state-gate message to satisfy rustfmt/LOC gate

cargo fmt --check was flagging the job-failure message line as over
the 100-char width, and work.rs is already pinned at the frozen
2100-line cap, so wrapping it would have failed the LOC gate instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Agentflare-Agent: claude-code
Agentflare-Branch: task/226-item-claim-doesn-t-check-item-state-a-st
Agentflare-Item: 226
Agentflare-Session: 7bda56eb-8cf5-4c3f-ba3e-b060851a47ec

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant