1. branch_list: Fix is_head comparison
- head_name now keeps full ref (e.g., "refs/heads/main")
- Previously stripped to short name causing is_head to always be false
2. branch_get/branch_exists: Fix name resolution for branches with '/'
- Previously, "feature/testing" was assumed to be a remote branch
- Now tries both refs/heads/ and refs/remotes/ candidates
- Correctly handles local branches like "feature/testing"
3. sync_refs: Fix upstream reference format
- Was storing "refs/remotes/{}/branch" (broken pattern)
- Now properly queries git2 for actual upstream branch name
sync_tags held git2 types (StringArray, Repository) across .await points
on sea-orm DB operations. Applied the same pattern as sync_refs:
- Added collect_tag_refs() sync helper that collects all tag metadata
(name, oid, description, tagger) into owned TagTip structs.
- sync_tags now calls collect_tag_refs() before any .await, so no git2
types cross the async boundary.
- Removed #[allow(dead_code)] from TagTip now that it's consumed.
- pool/worker.rs: only dispatch webhooks after sync succeeds (skip on error);
pass max_retries from PoolConfig to HookWorker
- sync/branch.rs: replaced with stub (sync_refs moved to commit.rs)
- sync/commit.rs: add collect_git_refs() that collects BranchTip/TagTip from
git2 entirely within one sync call; sync_refs now uses owned data so no
git2 types cross .await boundaries (future is Send)
- sync/fsck.rs: remove extraneous "HEAD" arg from rollback git update-ref
(correct syntax is: update-ref -m <msg> <ref> <new-sha> — no old-sha)
- webhook_dispatch.rs: touch_count uses Expr::col().add(1) for atomic
increment instead of overwriting with Expr::value(1)
- pool/mod.rs: pass config.redis_max_retries to HookWorker
- sync/mod.rs: wrap scan_skills_from_dir in spawn_blocking to avoid
blocking the async executor; use to_path_buf() to get owned PathBuf
- pool/worker.rs: replace 500ms poll sleep with cancellation-is_cancelled
check (eliminates artificial latency); add exponential backoff on Redis
errors (1s base, 32s cap, reset on success)
- pool/redis.rs: add 5s timeout on pool.get() for all three methods
(next, ack_raw, nak_with_retry) to prevent indefinite blocking on
unresponsive Redis
- sync/gc.rs: add comment explaining why git gc --auto non-zero exit
is benign
- webhook_dispatch.rs: remove unnecessary format! wrappers in slog macros
- config/hook.rs: document max_concurrent intent (K8s operator/HPA, not
the single-threaded worker itself)
- pool/mod.rs: pass shared http_client Arc to HookWorker
- worker.rs: remove double-locking (sync() manages its own lock),
await all webhook handles before returning, share http_client,
hoist namespace query out of loop
- redis.rs: atomic NAK via Lua script (LREM + LPUSH in one eval)
- sync/lock.rs: increase LOCK_TTL from 60s to 300s for large repos
- sync/mod.rs: split sync/sync_work, fsck_only/fsck_work, gc_only/gc_work
so callers can choose locked vs lock-free path; run_gc + sync_skills
outside the DB transaction
- hook/mod.rs: remove unused http field from HookService
- ssh/mod.rs, http/mod.rs: remove unused HookService/http imports
- pool/worker.rs: single-threaded consumer that BLMPOPs from Redis queues
sequentially. K8s replicas provide HA — each pod runs one worker.
- pool/redis.rs: RedisConsumer with BLMOVE atomic dequeue, ACK/NAK, and
retry-with-json support.
- pool/types.rs: HookTask, TaskType, PoolConfig (minimal — no pool metrics).
- sync/lock.rs: Redis SET NX EX per-repo lock to prevent concurrent workers
from processing the same repo. Lock conflicts are handled by requeueing
without incrementing retry count.
- hook/mod.rs: HookService.start_worker() spawns the background worker.
- ssh/mod.rs / http/mod.rs: ReceiveSyncService RPUSHes to Redis queue.
Both run_http and run_ssh call start_worker() to launch the consumer.
- Lock conflicts (GitError::Locked) in the worker are requeued without
incrementing retry_count so another worker can pick them up.
Fail fast if repo storage_path does not exist on disk, before
blocking the thread pool with spawn_blocking. This prevents
invalid tasks from consuming blocking threads while failing.
- Fix match pattern: `Ok(Ok(Err(_)))` must be treated as NOT a panic,
since execute_task_body returns Err(()) on task failure (not a panic).
Previously the Err path incorrectly set panicked=true, which could
cause pool to appear unhealthy.
- Add "task failed" log before retry/discard decision so real error
is visible in logs (previously only last_error on exhausted-retries
path was logged, which required hitting 5 retries to see the cause).
- Convert 3 remaining pool/mod.rs shorthand logs to format!() pattern.
- Add retry_count to HookTask with serde(default) for backwards compat
- Limit hook task retries to MAX_RETRIES=5, discard after limit to prevent
infinite requeue loops that caused 'task nack'd and requeued' log spam
- Add nak_with_retry() in RedisConsumer to requeue with incremented count
- Standardize all slog logs: replace "info!(l, "msg"; "k" => v)" shorthand
with "info!(l, "{}", format!("msg k={}", v))" across ssh/authz.rs,
ssh/handle.rs, ssh/server.rs, hook/webhook_dispatch.rs, hook/pool/mod.rs