Skip to content

fix(goal,hooks): correct timeout units and permission aggregation - #40

Merged
LeXwDeX merged 1 commit into
devfrom
fix/goal-and-hook-bugs
Jul 3, 2026
Merged

fix(goal,hooks): correct timeout units and permission aggregation#40
LeXwDeX merged 1 commit into
devfrom
fix/goal-and-hook-bugs

Conversation

@LeXwDeX

Copy link
Copy Markdown
Owner

Summary

Five fixes found by cross-review (FABLE5 model), verified against current code, applied.

Goal module (2 fixes)

G1: Judge timeout unit mismatch — DEFAULT_JUDGE_TIMEOUT was 30_000 (ms) used as Effect.timeout("30000 seconds") = 8.3h

Before:DEFAULT_JUDGE_TIMEOUT = 30_000Effect.timeout(${opts.timeout} seconds)"30000 seconds" = effectively no timeout. A hanging provider would stall the goal loop for hours.

After:DEFAULT_JUDGE_TIMEOUT_SECONDS = 30Effect.timeout("30 seconds"). Unit is now consistent.

G2: Pause message referenced non-existent config

Before:goal.ts:391 told users to "配置 auxiliary.goalJudge" — but the code never reads this config (judge always uses provider.defaultModel()). Users following the hint would see no effect.

After: Message now says "检查模型配置或换用更可靠的模型" (generic guidance).

Hooks module (3 fixes)

H1: Agent handler timeout missing * 1000

Before:settings.ts:1248entry.timeout ?? DEFAULT_AGENT_TIMEOUT_MS — the ONLY handler that didn't multiply seconds by 1000. A user configuring timeout: 120 (intending 120 seconds) got 120ms.

After:entry.timeout ? entry.timeout * 1000 : DEFAULT_AGENT_TIMEOUT_MS — matches all other handler types (command/mcp/http/prompt).

H2: permissionDecision was last-write-wins (security semantics)

Before:settings.ts:1528 — each hook's permissionDecision overwrote the previous. A later hook's allow could override an earlier hook's deny.

After: Most-restrictive-wins (deny > ask > allow). Once any hook says deny, it stays deny. Matches Claude Code permission semantics.

H3: MCP re-entry guard was dead code

Before:settings.ts:1053if (inHook) guard, but runEntry always passed false for inHook (line 1480). The guard never fired.

After: Guard removed; parameter renamed to _inHook (matching all other handlers).

Findings NOT fixed (assessed as not actionable)

IDClaimAssessment
G3done branch cleanup raceSpeculative — noReply messages don't trigger idle events; afterIdle is already in a forked fiber; deleteAndPublishDone is fiber-safe
H4seen Set instance-globalPer-directory (InstanceState), not per-session — needs product decision, not a clear bug
H5Settings loaded onceCovered by existing hook/extensions/hot-reload.ts which watches settings files

Verification

  • bun run typecheck: 0 errors
  • bun test test/goal/: 56 pass, 0 fail
  • Pre-push hook: turbo typecheck FULL TURBO (all cached)

Note

The previous dev CI run (PR #39) had 1 test failure in attention.test.ts (TUI sound notification mock) — pre-existing, unrelated to goal-loop. 3079/3104 tests passed including all 56 goal tests.

Goal:
- judge timeout: DEFAULT_JUDGE_TIMEOUT was 30_000 (ms) used as
"30000 seconds" in Effect.timeout — effectively 8.3h with no
timeout. Changed to DEFAULT_JUDGE_TIMEOUT_SECONDS = 30.
- pause message: removed misleading reference to non-existent
auxiliary.goalJudge config (code never reads it; judge always
uses provider.defaultModel).
Hooks:
- agent handler timeout: entry.timeout was used raw (seconds as
ms). Added missing * 1000 to match all other handler types.
- permissionDecision: was last-write-wins (later hook's allow
could overwrite earlier hook's deny). Changed to most-restrictive-
wins (deny > ask > allow), matching Claude Code semantics.
- mcp handler: removed dead re-entry guard (inHook was always false
from runEntry call site; guard never fired).
Found by cross-review (FABLE5 model). Verified against current code.
@LeXwDeX
LeXwDeX merged commit 7767ef7 into devJul 3, 2026
@LeXwDeX
LeXwDeX deleted the fix/goal-and-hook-bugs branch July 3, 2026 01:06
Sign up for freeto 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

@LeXwDeX