Skip to content

fix: post-1.0.0 code review — ponytail custom skills, auth health scoring, CI defects - #94

Merged
getappz merged 8 commits into
masterfrom
fix/post-1.0-review-fixes
Jul 8, 2026
Merged

fix: post-1.0.0 code review — ponytail custom skills, auth health scoring, CI defects#94
getappz merged 8 commits into
masterfrom
fix/post-1.0-review-fixes

Conversation

@getappz

@getappz getappz commented Jul 8, 2026

Copy link
Copy Markdown
Owner

What

High-effort code review of all commits after v1.0.0 (149 commits, 127 files); 12 verified findings fixed across four logical commits.

Critical

  • Ponytail custom skills (feat(ponytail): custom skills system + mode shortcuts #90) were dead on arrival: custom names were rejected at every dispatch point (normalize_config_mode only knows built-ins), collapsed to the default mode in build(), and the authored body was never delivered. Now wired end-to-end via normalize_extended_mode + inline body delivery; reserved names can't be shadowed by custom files.
  • Session mode leaked into the global default: no SessionEnd hook exists, and SessionStart promoted a stale session override into the global flag. SessionStart now clears the override first.
  • winget publish failed on every release: asset filter matched .exe|.msi but releases ship a .zip.

Major

  • Auth health scoring was unwired: record_error had zero production callers, last_used_at was never written → smart_pick scored every profile identically. Runner now records errors on rate-limit/failure exits; activation stamps last_used_at.
  • cargo-audit required-check deadlock: path filter removed so the required check reports on every PR (bit us live on fix(ponytail): E0593 compile error + restore AGENTS.md fallback lost in #90 #93).
  • ppa-publish: upload-artifact pinned to a malformed 42-char SHA (unresolvable).
  • docker: latest tag gated on a condition that's always false under the workflow's only trigger.
  • Pricing prefix match was nondeterministic: longest prefix now wins instead of random HashMap order (a wrong rate would be baked into the rollup cost cache).
  • Test env-mutation UB race: paths.rs and agents.rs serialized set_var under two different locks in one test binary; unified on agent-registry's PATH_LOCK.

Minor

  • Rollup sync prunes rows for deleted session files (costs no longer overstated forever; stale dedup ownership released).
  • MCP get_routing_suggestion honors AGENTFLARE_ROUTER via active_router() like the CLI hook.
  • skill-help.md documented the wrong config path/key; vendored-file-warning exempts bots; agent-registry mechanical clippy fixes.

Deliberately not fixed (tracked)

filter_skill_body prose false-positives (marker syntax is a product decision) · AUR pinned at 1.0.1 with no release automation · staged errors.rs enums / unused RouteContext fields (intentional per commit history).

Testing

cargo test --workspace: 269 passed / 0 failed. cargo build: zero errors, no new warnings. New behavior covered by existing suites; ponytail 33/33.

Summary by CodeRabbit

  • New Features
    • Extended ponytail mode selection to support custom mode/skill names, with improved default-mode normalization.
  • Bug Fixes
    • Clear session-scoped ponytail overrides between sessions.
    • Make pricing prefix matching deterministic.
    • Prune deleted session files from sync/catalog data.
    • Record “last used” auth metadata on activation.
  • Documentation
    • Updated default-mode configuration instructions (new path and key).
  • Build/Release
    • Installer verifies checksums by default (override available); Docker now runs as non-root.
    • Release/publish workflows tightened permissions, adjusted asset selection, and ensured Docker “latest” is applied consistently.

getappz added 4 commits July 8, 2026 11:46
…ng to global

- custom skill names now dispatch: normalize_extended_mode accepts
  runtime-discovered names in switcher, session, and default paths
- build() delivers the custom skill body inline instead of collapsing
  to the default mode and pointing at a nonexistent harness skill
- reserved built-in names (full.md etc.) can no longer shadow modes
- SessionStart clears the session-scoped override (no SessionEnd hook
  exists, so a stale override was promoted into the global flag)
- skill-help.md documented the wrong config path and JSON key
record_error had no production callers and last_used_at was never
written, so smart_pick scored every profile identically. The runner now
records errors on rate-limit and failure exits; activation stamps
last_used_at via the new touch_last_used.
… env lock

- lookup_pricing: longest matching prefix wins instead of random
  HashMap iteration order picking an arbitrary rate
- rollup sync prunes catalog/rollup/dedup rows for deleted session
  files (stale rows overstated cost forever and could suppress lines
  of new files as false duplicates)
- get_routing_suggestion uses active_router() like the CLI hook
- paths.rs test env mutation now shares agent-registry's PATH_LOCK:
  two independent locks let set_var calls race across threads
- agent-registry: mechanical clippy fixes
- winget: asset filter matched only .exe/.msi but releases ship a .zip,
  so every release run threw 'No winget installer assets found'
- security-check: drop paths filter — cargo audit is a required check
  and never reported on PRs not touching manifests, deadlocking merges
- ppa-publish: upload-artifact pinned to a malformed 42-char SHA
- docker: 'latest' tag was gated on a condition that is always false
  under the workflow's only trigger
- vendored-file-warning: exempt bot authors (Renovate)
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e8ed6529-5537-4d99-ac9f-4c7ff8b6c3fc

📥 Commits

Reviewing files that changed from the base of the PR and between becdbee and 0e4052f.

📒 Files selected for processing (3)
  • .github/workflows/ppa-publish.yml
  • .github/workflows/release.yml
  • .github/workflows/winget.yml

📝 Walkthrough

Walkthrough

This PR updates workflow and release tooling, hardens install/container behavior, and changes ponytail, auth, routing, pricing, and rollup logic.

Changes

CI, container, and release tooling

Layer / File(s) Summary
Workflow rules and asset matching
.github/workflows/docker.yml, .github/workflows/security-check.yml, .github/workflows/vendored-file-warning.yml, .github/workflows/winget.yml, .github/workflows/ppa-publish.yml, .github/workflows/release.yml
Docker latest tagging is always enabled, security checks run on push and pull request, vendored-file warnings exclude bot authors, winget asset matching is broadened, release permissions are narrowed by scope, and two workflow actions are pinned to different commits.
Container and release script changes
Dockerfile, install.sh, scripts/release-npm.sh
The container runs as a non-root user, install script location and verification handling change, and npm release downloads are refreshed and fail when platform assets are skipped.
Local tooling cleanup
.gitignore, install_onpush.ps1, run_onpush.bat
Local onpush tooling files are ignored or removed.

Runtime behavior changes

Layer / File(s) Summary
Agent registry annotations and refactors
crates/agent-registry/src/detect.rs, crates/agent-registry/src/registry.rs
Public helpers and registry accessors gain #[must_use], and two internal refactors keep the same behavior.
Extended ponytail mode normalization
crates/ponytail/src/config.rs, crates/ponytail/src/sub_skills.rs, crates/ponytail/src/skill-help.md
Ponytail accepts extended modes and custom skill names, avoids reserved-name shadowing, and updates the documented config path and key.
Instructions build path for custom skills
crates/ponytail/src/instructions.rs
Ponytail instructions now branch between installed skills, inline custom skills, and the existing fallback chain using an owned effective mode.
Switcher and session handling
crates/ponytail/src/switcher.rs, src/cli/ponytail.rs
Mode parsing uses the extended normalizer in multiple paths, and session start clears stale overrides before reading the active mode.
Auth profile health tracking
src/auth.rs, src/auth_db.rs, src/auth_runner.rs
Auth activation updates last-used metadata, and the runner records stderr for rate-limit and failure exits while changing exit categorization arguments.
Router, test lock, pricing, and rollup updates
src/mcp_server.rs, src/paths.rs, src/pricing.rs, src/rollup.rs
Routing uses the active router, tests share the PATH lock, pricing chooses the longest matching prefix, and rollup sync prunes stale catalog rows.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
    participant AuthCLI as auth CLI
    participant ActivateWith as activate_with
    participant AuthDB as auth_db
    participant AuthRunner as auth_runner
    AuthCLI->>ActivateWith: activate profile
    ActivateWith->>AuthDB: touch_last_used(agent, profile)
    ActivateWith-->>AuthCLI: activation result
    AuthRunner->>AuthRunner: spawn_and_capture
    AuthRunner->>AuthRunner: categorize_exit(code, stderr)
    alt RateLimited
        AuthRunner->>AuthDB: record_error(agent, profile, stderr)
    else Failure
        AuthRunner->>AuthDB: record_error(agent, profile, stderr)
    end
Loading
sequenceDiagram
    participant User as user
    participant Switcher as switcher::detect
    participant Config as config::normalize_extended_mode
    participant SubSkills as sub_skills
    participant Instructions as instructions::build
    User->>Switcher: /ponytail:{name}
    Switcher->>Config: normalize_extended_mode(name)
    Config->>SubSkills: get_custom(name) when needed
    Config-->>Switcher: normalized mode
    Switcher->>Instructions: build(mode, skill_path)
    Instructions->>SubSkills: get(effective)
    alt installed skill
        SubSkills-->>Instructions: skill body
    else custom skill
        Instructions->>SubSkills: get_custom(effective)
        SubSkills-->>Instructions: custom body
    end
Loading

Possibly related issues

Possibly related PRs

  • getappz/agentflare#34: Both PRs touch src/auth_runner.rs and the rate-limit / failure categorization path.
  • getappz/agentflare#45: Both PRs modify the same ponytail runtime modules and mode-handling paths.
  • getappz/agentflare#60: Both PRs touch crates/ponytail/src/instructions.rs and the build() logic around installed skill handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main changes: ponytail custom skills, auth health scoring, and CI fixes.
Description check ✅ Passed The PR explains the changes and testing, but it doesn't follow the template headings and omits explicit risk/backward-compatibility notes.
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/post-1.0-review-fixes

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

getappz added 3 commits July 8, 2026 11:50
onpush_run.log, install_onpush.ps1, and run_onpush.bat are machine-local
doc-generation automation (run_onpush.bat hard-codes a local path); they
were swept into 853e4c3 by accident. Removed from tracking and
gitignored along with the rest of the onpush artifacts.
zizmor's impostor-commit audit flagged the old pin: the SHA resolves via
the commits API (fork network) but is not in dtolnay/rust-toolchain's
actual branch history. Repinned to the current stable branch head.
- Dockerfile: install.sh was fetched from a nonexistent 'main' branch so
  the image could never build; also drop unused sudo and run as a
  non-root user
- install.sh: fail closed when SHA256SUMS is missing or lacks the asset
  line (AGENTFLARE_SKIP_VERIFY=1 to bypass); never source-build from an
  incidental pwd when piped via curl|sh
- release-npm.sh: always re-download release assets (stale unversioned
  files in a reused RELEASE_DIR republished old binaries); exit non-zero
  when any declared platform asset is missing instead of publishing an
  incomplete set
@getappz

getappz commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

Follow-up commits after the gap-closure review of scripts/Docker/build.rs (files the first pass didn't cover):

  • 977030b — removed accidentally committed local onpush tooling (onpush_run.log, install_onpush.ps1, run_onpush.bat — the .bat hard-codes a local Windows path; swept in via 853e4c3) and gitignored the artifacts. No secrets in the leaked files, only local paths; they remain in git history.
  • 60d9ef3 — zizmor impostor-commit fix: dtolnay/rust-toolchain was pinned to a SHA not in that repo's branch history (resolves only via the fork network — the exact supply-chain pattern zizmor flags). Repinned to the genuine stable branch head.
  • becdbee — tooling hardening: Dockerfile could never build (fetched install.sh from nonexistent main branch); dropped unused sudo + now runs as non-root. install.sh now fails closed when SHA256SUMS is missing/incomplete (AGENTFLARE_SKIP_VERIFY=1 to bypass) and never source-builds from an incidental pwd when piped. release-npm.sh always re-downloads assets (stale unversioned files in a reused RELEASE_DIR republished old binaries) and exits non-zero if any declared platform asset is missing.

Not fixed (tracked): Docker image builds aren't reproducible (installs latest from master at build time — needs a version build-arg through install.sh); build.rs and config files came back clean.

@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: 4

🧹 Nitpick comments (2)
crates/ponytail/src/switcher.rs (1)

100-149: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a test for custom-skill-name dispatch.

Existing tests (detects_mode_switch, detects_default, detects_session_mode) only cover the fixed lite/full/ultra modes. Since this PR's core fix is custom-mode dispatch via normalize_extended_mode, a test exercising SwitchAction::SetMode/SetDefault/SetSession with a custom skill name (via a mocked get_custom) would directly validate the fix.

🤖 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 `@crates/ponytail/src/switcher.rs` around lines 100 - 149, Add a test in
switcher.rs that exercises custom-skill-name dispatch through detect and
normalize_extended_mode by mocking get_custom to return a custom skill name;
verify SwitchAction::SetMode, SwitchAction::SetDefault, and
SwitchAction::SetSession resolve correctly for that custom name instead of only
the built-in lite/full/ultra cases. Place it alongside the existing
detects_mode_switch, detects_default, and detects_session_mode tests so the new
coverage directly validates the PR’s custom mode fix.
src/auth_runner.rs (1)

15-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Health-scoring wiring looks correct; consider deduplicating the repeated recording pattern.

The (code, stderr) capture and categorize_exit(code, &stderr) call are correct, and both RateLimited (Lines 30-34) and Failure (Lines 42-48) branches correctly reuse the same connection for their respective operations with proper parameter passing to record_error.

The let conn = auth_db::open_or_rebuild(); if let Some((profile, _)) = auth_db::get_rotation_last(&conn, agent) { auth_db::record_error(&conn, agent, &profile, &stderr); ... } pattern is duplicated across both branches. Extracting a small helper (e.g., record_last_profile_error(agent, stderr) -> Option<Connection>) would reduce duplication if this logic grows further.

🤖 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 `@src/auth_runner.rs` around lines 15 - 53, The repeated auth_db recording
logic in auth_runner::run should be deduplicated. Extract the shared pattern
that opens the connection, looks up the last rotated profile with
auth_db::get_rotation_last, and records the stderr via auth_db::record_error
into a small helper (for example, a helper tied to run or auth_db). Then update
both the ExitKind::RateLimited and ExitKind::Failure branches to call that
helper, keeping the existing behavior unchanged while removing the duplicated
block.
🤖 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 `@crates/ponytail/src/config.rs`:
- Around line 24-31: Update the mode parsing used by PonytailAction::Set and
PonytailAction::Default so it routes through normalize_extended_mode instead of
normalize_config_mode. The current path in the cli/ponytail handling rejects
runtime-discovered custom skill names as invalid before they can reach
set_default_mode or set_active, so make the action-specific parsing consistent
with PromptSubmit by accepting both built-in and custom modes via the extended
helper.

In `@crates/ponytail/src/sub_skills.rs`:
- Around line 39-43: The reserved-name guard in sub_skills.rs currently compares
path.file_stem() directly against VALID_MODES, so mixed-case names can bypass
the check and be added to CUSTOM_SKILLS. Update the name handling in the
custom-skill discovery path to normalize the stem before the
VALID_MODES.contains(...) check, using the existing logic around
path.file_stem() and the CUSTOM_SKILLS insertion flow, so reserved built-in
modes remain blocked case-insensitively.

In `@src/auth.rs`:
- Around line 210-212: The success path in auth handling is blocked by the DB
health update, so move the success output to happen before the call into
auth_db::touch_last_used, or make that touch best-effort after printing success.
Use the open_or_rebuild() flow in src/auth.rs and the touch_last_used helper as
the key points to reorder so a transient auth.db failure cannot prevent the
success message from being emitted.

In `@src/rollup.rs`:
- Around line 219-228: The per-statement `.ok()` calls inside the stale-path
cleanup are swallowing errors and breaking the atomicity of the transaction.
Update the cleanup logic in the function that iterates over `stale` to follow
the same fail-fast pattern used by `reindex_file`: if any `tx.execute(...)` call
fails, abort the function immediately so the transaction rolls back on drop,
instead of continuing and committing partial deletes. Keep the
`conn.transaction()` / `tx.commit()` flow, but ensure a failure in any of the
`DELETE` statements prevents commit.

---

Nitpick comments:
In `@crates/ponytail/src/switcher.rs`:
- Around line 100-149: Add a test in switcher.rs that exercises
custom-skill-name dispatch through detect and normalize_extended_mode by mocking
get_custom to return a custom skill name; verify SwitchAction::SetMode,
SwitchAction::SetDefault, and SwitchAction::SetSession resolve correctly for
that custom name instead of only the built-in lite/full/ultra cases. Place it
alongside the existing detects_mode_switch, detects_default, and
detects_session_mode tests so the new coverage directly validates the PR’s
custom mode fix.

In `@src/auth_runner.rs`:
- Around line 15-53: The repeated auth_db recording logic in auth_runner::run
should be deduplicated. Extract the shared pattern that opens the connection,
looks up the last rotated profile with auth_db::get_rotation_last, and records
the stderr via auth_db::record_error into a small helper (for example, a helper
tied to run or auth_db). Then update both the ExitKind::RateLimited and
ExitKind::Failure branches to call that helper, keeping the existing behavior
unchanged while removing the duplicated block.
🪄 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: a53fc6a8-594c-436e-98c3-7472fd3f52f7

📥 Commits

Reviewing files that changed from the base of the PR and between fc74f23 and 3d3024f.

📒 Files selected for processing (20)
  • .github/workflows/docker.yml
  • .github/workflows/ppa-publish.yml
  • .github/workflows/security-check.yml
  • .github/workflows/vendored-file-warning.yml
  • .github/workflows/winget.yml
  • crates/agent-registry/src/detect.rs
  • crates/agent-registry/src/registry.rs
  • crates/ponytail/src/config.rs
  • crates/ponytail/src/instructions.rs
  • crates/ponytail/src/skill-help.md
  • crates/ponytail/src/sub_skills.rs
  • crates/ponytail/src/switcher.rs
  • src/auth.rs
  • src/auth_db.rs
  • src/auth_runner.rs
  • src/cli/ponytail.rs
  • src/mcp_server.rs
  • src/paths.rs
  • src/pricing.rs
  • src/rollup.rs

Comment on lines +24 to +31
/// Like `normalize_config_mode`, but also accepts user-defined custom skill
/// names (which are discovered at runtime, so they can't be `&'static str`).
pub fn normalize_extended_mode(mode: &str) -> Option<String> {
let m = mode.trim().to_lowercase();
normalize_config_mode(&m)
.map(str::to_string)
.or_else(|| crate::sub_skills::get_custom(&m).map(|_| m))
}

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== config.rs matches ==\n'
rg -n "normalize_extended_mode|normalize_config_mode|get_custom|set_default_mode|set_active" crates/ponytail/src/config.rs src/cli/ponytail.rs || true

printf '\n== config.rs excerpt ==\n'
sed -n '1,120p' crates/ponytail/src/config.rs

printf '\n== cli excerpt ==\n'
sed -n '1,240p' src/cli/ponytail.rs

Repository: getappz/agentflare

Length of output: 13666


Wire PonytailAction::Set/Default through normalize_extended_modesrc/cli/ponytail.rs:104-125 still uses normalize_config_mode, so direct set/default commands reject custom skill names as invalid mode before they reach set_default_mode/set_active. PromptSubmit already uses the extended path.

🤖 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 `@crates/ponytail/src/config.rs` around lines 24 - 31, Update the mode parsing
used by PonytailAction::Set and PonytailAction::Default so it routes through
normalize_extended_mode instead of normalize_config_mode. The current path in
the cli/ponytail handling rejects runtime-discovered custom skill names as
invalid before they can reach set_default_mode or set_active, so make the
action-specific parsing consistent with PromptSubmit by accepting both built-in
and custom modes via the extended helper.

Comment on lines +39 to +43
// Reserved built-in mode/skill names can't be shadowed by
// custom files — a user's full.md must not hijack full mode.
if crate::config::VALID_MODES.contains(&name) {
continue;
}

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm VALID_MODES contents and whether reserved skill names overlap.
rg -n 'VALID_MODES' crates/ponytail/src/config.rs -A5 -B2
rg -n 'fn normalize_mode|fn all_mode_names|fn all_skill_names' crates/ponytail/src/config.rs crates/ponytail/src/switcher.rs -A10

Repository: getappz/agentflare

Length of output: 2825


🏁 Script executed:

#!/bin/bash
sed -n '1,220p' crates/ponytail/src/sub_skills.rs
sed -n '1,120p' crates/ponytail/src/config.rs
sed -n '1,120p' crates/ponytail/src/switcher.rs

Repository: getappz/agentflare

Length of output: 13335


Normalize the reserved-name check crates/ponytail/src/sub_skills.rs:39-43
path.file_stem() is compared raw against VALID_MODES, so Full.md/FULL.md slips past the guard and gets added to CUSTOM_SKILLS. Normalize the stem before checking so reserved names stay blocked case-insensitively.

🤖 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 `@crates/ponytail/src/sub_skills.rs` around lines 39 - 43, The reserved-name
guard in sub_skills.rs currently compares path.file_stem() directly against
VALID_MODES, so mixed-case names can bypass the check and be added to
CUSTOM_SKILLS. Update the name handling in the custom-skill discovery path to
normalize the stem before the VALID_MODES.contains(...) check, using the
existing logic around path.file_stem() and the CUSTOM_SKILLS insertion flow, so
reserved built-in modes remain blocked case-insensitively.

Comment thread src/auth.rs
Comment on lines +210 to +212
let conn = crate::auth_db::open_or_rebuild();
crate::auth_db::touch_last_used(&conn, agent, &profile);

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:

#!/bin/bash
set -euo pipefail

printf '--- src/auth.rs outline ---\n'
ast-grep outline src/auth.rs --view expanded || true

printf '\n--- src/auth_db.rs outline ---\n'
ast-grep outline src/auth_db.rs --view expanded || true

printf '\n--- relevant references to open_or_rebuild/touch_last_used/activate ---\n'
rg -n "open_or_rebuild|touch_last_used|activate|restored|success" src/auth.rs src/auth_db.rs src/auth_runner.rs

printf '\n--- src/auth.rs around the reported lines ---\n'
sed -n '180,240p' src/auth.rs

printf '\n--- src/auth_db.rs around open_or_rebuild/touch_last_used ---\n'
sed -n '1,220p' src/auth_db.rs

printf '\n--- src/auth_runner.rs relevant branch ---\n'
sed -n '1,220p' src/auth_runner.rs

Repository: getappz/agentflare

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- src/auth.rs outline ---'
ast-grep outline src/auth.rs --view expanded || true

printf '\n%s\n' '--- src/auth_db.rs outline ---'
ast-grep outline src/auth_db.rs --view expanded || true

printf '\n%s\n' '--- relevant references to open_or_rebuild/touch_last_used/activate ---'
rg -n "open_or_rebuild|touch_last_used|activate|restored|success" src/auth.rs src/auth_db.rs src/auth_runner.rs

printf '\n%s\n' '--- src/auth.rs around the reported lines ---'
sed -n '180,240p' src/auth.rs

printf '\n%s\n' '--- src/auth_db.rs around open_or_rebuild/touch_last_used ---'
sed -n '1,220p' src/auth_db.rs

printf '\n%s\n' '--- src/auth_runner.rs relevant branch ---'
sed -n '1,220p' src/auth_runner.rs

Repository: getappz/agentflare

Length of output: 24883


Move the success output before the DB touch. src/auth.rs:210-220open_or_rebuild() can still panic on open/migration failures, so a transient auth.db issue can turn a successful restore into a crash before the success message is printed. Make this health update best-effort, or at least emit the success output before calling into the DB.

🤖 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 `@src/auth.rs` around lines 210 - 212, The success path in auth handling is
blocked by the DB health update, so move the success output to happen before the
call into auth_db::touch_last_used, or make that touch best-effort after
printing success. Use the open_or_rebuild() flow in src/auth.rs and the
touch_last_used helper as the key points to reorder so a transient auth.db
failure cannot prevent the success message from being emitted.

Comment thread src/rollup.rs
Comment on lines +219 to +228
let Ok(tx) = conn.transaction() else {
return;
};
for path in stale {
tx.execute("DELETE FROM session_files WHERE file_path = ?1", params![path]).ok();
tx.execute("DELETE FROM file_rollup WHERE file_path = ?1", params![path]).ok();
tx.execute("DELETE FROM dedup_keys WHERE file_path = ?1", params![path]).ok();
}
tx.commit().ok();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Per-statement .ok() breaks the transaction's atomicity guarantee.

Each DELETE inside the loop swallows its own error and the loop continues, so a mid-loop failure (lock contention, I/O error) can leave a stale path partially deleted (e.g. session_files row gone but file_rollup/dedup_keys rows still present) and tx.commit() will still succeed. Since the path is then gone from session_files, the next sync() run's known list won't include it either, so it will never be pruned again — permanently reintroducing the exact "stale rows overstate cost forever" bug the doc comment (lines 198-200) says this function prevents.

reindex_file in this same file already uses the correct pattern (if tx.execute(...).is_err() { return; }, letting the transaction roll back on drop) — this function should follow the same convention.

🔧 Proposed fix to abort on per-statement failure
     let Ok(tx) = conn.transaction() else {
         return;
     };
     for path in stale {
-        tx.execute("DELETE FROM session_files WHERE file_path = ?1", params![path]).ok();
-        tx.execute("DELETE FROM file_rollup WHERE file_path = ?1", params![path]).ok();
-        tx.execute("DELETE FROM dedup_keys WHERE file_path = ?1", params![path]).ok();
+        if tx.execute("DELETE FROM session_files WHERE file_path = ?1", params![path]).is_err() {
+            return;
+        }
+        if tx.execute("DELETE FROM file_rollup WHERE file_path = ?1", params![path]).is_err() {
+            return;
+        }
+        if tx.execute("DELETE FROM dedup_keys WHERE file_path = ?1", params![path]).is_err() {
+            return;
+        }
     }
     tx.commit().ok();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let Ok(tx) = conn.transaction() else {
return;
};
for path in stale {
tx.execute("DELETE FROM session_files WHERE file_path = ?1", params![path]).ok();
tx.execute("DELETE FROM file_rollup WHERE file_path = ?1", params![path]).ok();
tx.execute("DELETE FROM dedup_keys WHERE file_path = ?1", params![path]).ok();
}
tx.commit().ok();
}
let Ok(tx) = conn.transaction() else {
return;
};
for path in stale {
if tx.execute("DELETE FROM session_files WHERE file_path = ?1", params![path]).is_err() {
return;
}
if tx.execute("DELETE FROM file_rollup WHERE file_path = ?1", params![path]).is_err() {
return;
}
if tx.execute("DELETE FROM dedup_keys WHERE file_path = ?1", params![path]).is_err() {
return;
}
}
tx.commit().ok();
}
🤖 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 `@src/rollup.rs` around lines 219 - 228, The per-statement `.ok()` calls inside
the stale-path cleanup are swallowing errors and breaking the atomicity of the
transaction. Update the cleanup logic in the function that iterates over `stale`
to follow the same fail-fast pattern used by `reindex_file`: if any
`tx.execute(...)` call fails, abort the function immediately so the transaction
rolls back on drop, instead of continuing and committing partial deletes. Keep
the `conn.transaction()` / `tx.commit()` flow, but ensure a failure in any of
the `DELETE` statements prevents commit.

@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: 1

🧹 Nitpick comments (1)
Dockerfile (1)

5-14: 🧹 Nitpick | 🔵 Trivial

Non-root user setup looks correct.

apt-get steps still run as root before the USER switch, and PATH/WORKDIR are consistently updated for the new user.

Reminder: image builds remain non-reproducible.

Line 12 still installs from master at build time, so image builds aren't pinned to a specific release, which the PR itself flags as a known gap requiring a version build-arg threaded through install.sh.

🤖 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 `@Dockerfile` around lines 5 - 14, The non-root setup is fine, but the install
step in the Dockerfile still pulls install.sh from the moving master branch,
making builds non-reproducible. Update the Dockerfile’s curl | sh install step
to use a pinned release/version source instead of master, and thread that
version through install.sh using the existing version build-arg approach so the
install is deterministic.
🤖 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 `@install.sh`:
- Around line 138-157: The fail-closed verification flow still allows unverified
installs when neither checksum tool is present because verify_checksum()
succeeds in that case. Update verify_checksum() so the missing-tool path is
treated like any other verification failure unless AGENTFLARE_SKIP_VERIFY=1 is
explicitly set, and make sure the install.sh checksum handling around
verify_checksum and the SHA256SUMS download branch consistently enforces the
same opt-in bypass.

---

Nitpick comments:
In `@Dockerfile`:
- Around line 5-14: The non-root setup is fine, but the install step in the
Dockerfile still pulls install.sh from the moving master branch, making builds
non-reproducible. Update the Dockerfile’s curl | sh install step to use a pinned
release/version source instead of master, and thread that version through
install.sh using the existing version build-arg approach so the install is
deterministic.
🪄 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: 2d5d8f83-d0ca-43ff-8acb-45d92df98e13

📥 Commits

Reviewing files that changed from the base of the PR and between 3d3024f and becdbee.

⛔ Files ignored due to path filters (1)
  • onpush_run.log is excluded by !**/*.log
📒 Files selected for processing (7)
  • .github/workflows/ppa-publish.yml
  • .gitignore
  • Dockerfile
  • install.sh
  • install_onpush.ps1
  • run_onpush.bat
  • scripts/release-npm.sh
💤 Files with no reviewable changes (2)
  • run_onpush.bat
  • install_onpush.ps1
✅ Files skipped from review due to trivial changes (1)
  • .gitignore

Comment thread install.sh
Comment on lines +138 to +157
# Fail closed: an installer that silently skips verification when the
# (much smaller) SHA256SUMS request is blocked or incomplete is exactly
# what a selective MITM wants. AGENTFLARE_SKIP_VERIFY=1 is the escape hatch.
if curl -fsSL "$sums_url" -o "$tmpdir/SHA256SUMS" 2>/dev/null; then
expected="$(grep "agentflare-${target}.tar.gz" "$tmpdir/SHA256SUMS" | cut -d' ' -f1)"
if [ -n "$expected" ]; then
verify_checksum "$tmpdir/agentflare.tar.gz" "$expected"
elif [ "${AGENTFLARE_SKIP_VERIFY:-0}" = "1" ]; then
echo " Warning: agentflare-${target}.tar.gz not listed in SHA256SUMS — proceeding unverified (AGENTFLARE_SKIP_VERIFY=1)"
else
echo "Error: agentflare-${target}.tar.gz is not listed in SHA256SUMS — refusing to install unverified."
echo "Set AGENTFLARE_SKIP_VERIFY=1 to bypass (not recommended)."
exit 1
fi
elif [ "${AGENTFLARE_SKIP_VERIFY:-0}" = "1" ]; then
echo " Warning: checksums not available — proceeding unverified (AGENTFLARE_SKIP_VERIFY=1)"
else
echo " Warning: checksums not available, skipping verification"
echo "Error: could not download SHA256SUMS — refusing to install unverified."
echo "Set AGENTFLARE_SKIP_VERIFY=1 to bypass (not recommended)."
exit 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and inspect the installer around the cited lines.
git ls-files install.sh
wc -l install.sh
sed -n '80,170p' install.sh | cat -n

Repository: getappz/agentflare

Length of output: 4055


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for checksum helpers and any override usage.
rg -n "verify_checksum|AGENTFLARE_SKIP_VERIFY|sha256sum|shasum" install.sh

Repository: getappz/agentflare

Length of output: 1119


Gate the missing-hash-tool path behind the same opt-in

verify_checksum() still returns success when neither sha256sum nor shasum is installed, so a machine without either tool will proceed unverified unless AGENTFLARE_SKIP_VERIFY=1 is set. That leaves a bypass in the default fail-closed flow.

🤖 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 `@install.sh` around lines 138 - 157, The fail-closed verification flow still
allows unverified installs when neither checksum tool is present because
verify_checksum() succeeds in that case. Update verify_checksum() so the
missing-tool path is treated like any other verification failure unless
AGENTFLARE_SKIP_VERIFY=1 is explicitly set, and make sure the install.sh
checksum handling around verify_checksum and the SHA256SUMS download branch
consistently enforces the same opt-in bypass.

…-9, 11-14)

Workflow-level contents: read on release.yml, ppa-publish.yml, and
winget.yml, with per-job elevation only where the token is actually
used for writes: the release job (creates the release + uploads assets)
gets contents: write, the three dispatch jobs get actions: write.
Homebrew/Scoop jobs push with dedicated PATs, not GITHUB_TOKEN.
@getappz
getappz merged commit ea88354 into master Jul 8, 2026
10 checks passed
@getappz
getappz deleted the fix/post-1.0-review-fixes branch July 8, 2026 07:16
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant