Skip to content

feat: add Mobius Loop hooks (loop-detector, definition-gates)#1188

Open
agno01 wants to merge 6 commits intocode-yeongyu:devfrom
agno01:feat/mobius-loop-hooks
Open

feat: add Mobius Loop hooks (loop-detector, definition-gates)#1188
agno01 wants to merge 6 commits intocode-yeongyu:devfrom
agno01:feat/mobius-loop-hooks

Conversation

@agno01
Copy link

@agno01 agno01 commented Jan 28, 2026

Summary

Changes

  • src/hooks/loop-detector/ - Detects repeated calls, error loops, ping-pong patterns
  • src/hooks/definition-gates/ - DoR checks before delegation, DoD reminders on completion
  • src/hooks/index.ts - Export new hooks
  • src/config/schema.ts - Add hook names to HookNameSchema
  • src/index.ts - Initialize and wire hooks to lifecycle methods

Testing

bun test src/hooks/loop-detector/index.test.ts src/hooks/definition-gates/index.test.ts
# 18 pass, 0 fail

Related Issues

Closes #1177
Closes #1178


Summary by cubic

Adds two Mobius Loop–inspired hooks: loop-detector to prevent infinite tool loops and definition-gates to enforce Definition of Ready/Done. This reduces token waste and improves task quality during delegation and completion.

  • New Features

    • loop-detector: Detects repeated calls, repeated errors, and read/edit ping-pong; injects a stop-and-analyze warning.
    • definition-gates: Enforces DoR before delegation and DoD reminders on todo completion with concise checklists.
    • Registered both hooks (schema, exports) and wired into tool.execute.before and event flows; added focused tests.
  • Bug Fixes

Written for commit 50dee07. Summary will update on new commits.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 28, 2026

All contributors have signed the CLA. Thank you! ✅
Posted by the CLA Assistant Lite bot.

@agno01
Copy link
Author

agno01 commented Jan 28, 2026

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Jan 28, 2026
@agno01
Copy link
Author

agno01 commented Jan 28, 2026

recheck

@agno01
Copy link
Author

agno01 commented Jan 28, 2026

I have read the CLA Document and I hereby sign the CLA

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 7 files

Confidence score: 3/5

  • Definition-of-Ready checks can fail incorrectly because the ambiguity regex in src/hooks/definition-gates/index.ts matches “or” inside other words, leading to frequent false positives.
  • The dependencies_mapped criterion is hardcoded true in src/hooks/definition-gates/index.ts, so missing dependencies won’t block readiness as intended.
  • Given these medium-severity, user-facing validation issues, there’s some risk of incorrect gate outcomes if merged as-is.
  • Pay close attention to src/hooks/definition-gates/index.ts - ambiguity regex false positives and the hardcoded dependencies gate.
Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/hooks/definition-gates/index.ts">

<violation number="1" location="src/hooks/definition-gates/index.ts:143">
P2: Ambiguity regex matches `or` inside other words, causing frequent false positives and DoR failures.</violation>

<violation number="2" location="src/hooks/definition-gates/index.ts:149">
P2: `dependencies_mapped` criterion is always marked as met because `dependenciesMapped` is hardcoded to true, so the Definition-of-Ready check can never fail for missing dependencies.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

agno01 added a commit to agno01/oh-my-opencode that referenced this pull request Jan 28, 2026
- Use word boundaries for ambiguity detection to avoid false positives
  (e.g., 'factor' no longer triggers on 'or')
- Detect dependency mentions in prompt instead of hardcoding true
- Addresses cubic-dev-ai review comments on PR code-yeongyu#1188
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 7 files

Confidence score: 4/5

  • Medium-risk gap: src/hooks/loop-detector/index.test.ts never constructs a loop in oldHistory, so the “recent-history-window” case can falsely pass and fails to prove older loops are ignored.
  • Overall functional impact seems modest because the flaw is limited to a single test case, so the production loop detector logic is unaffected.
  • Pay close attention to src/hooks/loop-detector/index.test.ts - ensure the test actually simulates a loop in oldHistory to confirm the recent-history window behavior.
Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/hooks/loop-detector/index.test.ts">

<violation number="1" location="src/hooks/loop-detector/index.test.ts:97">
P2: The recent-history-window test doesn't create a loop in oldHistory, so it can't validate that older loops are ignored.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

agno01 added a commit to agno01/oh-my-opencode that referenced this pull request Jan 28, 2026
Create actual loop pattern (3 identical calls) in oldHistory to properly
test that loops outside the recent history window are not detected.
Uses varied tools in filler to avoid triggering alternating pattern.

Addresses cubic-dev-ai review on PR code-yeongyu#1188
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 7 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

code-yeongyu pushed a commit that referenced this pull request Jan 28, 2026
Copy link
Owner

@code-yeongyu code-yeongyu left a comment

Choose a reason for hiding this comment

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

Great feature addition for preventing infinite loops and enforcing quality gates!

Review notes:

The Mobius Loop hooks concept is solid:

  • loop-detector: Good pattern detection for repeated calls, error loops, and ping-pong patterns
  • definition-gates: DoR/DoD enforcement helps maintain task quality

cubic identified issues (now resolved in latest commit):

  • The ambiguity regex false positives were fixed
  • The hardcoded dependencies_mapped was addressed
  • Test coverage for recent-history-window was improved

Questions:

  1. Are these hooks enabled by default, or do users need to opt-in via config?
  2. What's the default threshold for loop detection (number of repeated calls before triggering)?
  3. Is there a way to customize the DoR/DoD checklists per project?

Overall this looks good. Once the questions above are clarified, I'm happy to approve.

Copy link
Owner

@code-yeongyu code-yeongyu left a comment

Choose a reason for hiding this comment

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

Approving this feature addition. The Mobius Loop hooks (loop-detector, definition-gates) are well-implemented.

Summary:

  • loop-detector prevents infinite tool loops (repeated calls, error loops, ping-pong patterns)
  • definition-gates enforces DoR before delegation and DoD reminders on completion
  • cubic review passed on latest commit (5/5 confidence)
  • CLA signed
  • 18 tests passing

The hooks address real problems (#1177, #1178) with token-wasting infinite loops.

Note for docs: Consider adding a brief section in configurations.md about these new hooks once merged.

@agno01
Copy link
Author

agno01 commented Jan 28, 2026

Thanks for the review and approval! Answering your questions:

1. Are these hooks enabled by default?

Yes, both hooks are enabled by default. The isHookEnabled() function checks if a hook is NOT in disabledHooks, so all hooks are active unless explicitly disabled.

Users can opt-out via config:

{
  "disabled_hooks": ["loop-detector", "definition-gates"]
}

2. What's the default threshold for loop detection?

From LOOP_THRESHOLDS:

  • Repeated calls: 3 identical tool calls (same tool + same args)
  • Error loop: 2 consecutive same error messages
  • Ping-pong pattern: 6 alternating calls (e.g., read-edit-read-edit-read-edit)
  • History window: Only considers last 10 calls

3. Is there a way to customize the DoR/DoD checklists per project?

Not currently. The checklists are hardcoded in formatCriterion(). If there's demand for customization, we could extend the config schema in a follow-up PR to allow per-project overrides.


Let me know if you'd like any changes before merge!

@agno01 agno01 requested a review from code-yeongyu January 28, 2026 16:42
@code-yeongyu
Copy link
Owner

@sisyphus-dev-ai please review this PR

@sisyphus-dev-ai sisyphus-dev-ai added the sisyphus: working Sisyphus is currently working on this label Jan 28, 2026
@sisyphus-dev-ai
Copy link
Collaborator

Code Review: Mobius Loop Hooks (loop-detector, definition-gates)

Reviewer: @sisyphus-dev-ai


Summary

This PR adds two new hooks to prevent infinite loops and enforce quality gates:

  • loop-detector: Detects repeated calls, error loops, and ping-pong patterns
  • definition-gates: Enforces Definition of Ready (DoR) before delegation and Definition of Done (DoD) reminders on completion

The feature addresses real problems documented in #1177 and #1178.


Review Findings

Critical Issue: TypeScript Import Errors

The new hooks have TypeScript compilation errors that need to be fixed:

src/hooks/definition-gates/index.ts(1,28): error TS2305: Module '"@opencode-ai/plugin"' has no exported member 'PreToolUseInput'.
src/hooks/definition-gates/index.ts(1,45): error TS2305: Module '"@opencode-ai/plugin"' has no exported member 'PreToolUseOutput'.
src/hooks/loop-detector/index.ts(1,28): error TS2305: Module '"@opencode-ai/plugin"' has no exported member 'PreToolUseInput'.
src/hooks/loop-detector/index.ts(1,45): error TS2305: Module '"@opencode-ai/plugin"' has no exported member 'PreToolUseOutput'.

Root Cause: The hooks incorrectly import PreToolUseInput and PreToolUseOutput from @opencode-ai/plugin, but these types don't exist in that module.

Fix Required: Follow the pattern used by other hooks (e.g., comment-checker, prometheus-md-only) which define inline types:

// Instead of:
import type { PluginInput, PreToolUseInput, PreToolUseOutput } from "@opencode-ai/plugin"

// Use:
import type { PluginInput } from "@opencode-ai/plugin"

// And define inline types for the hook signatures:
"tool.execute.before": async (
  input: { tool: string; sessionID: string; callID: string },
  output: { args: Record<string, unknown>; message?: string }
): Promise<void> => {

Or, for loop-detector which needs tool.name and tool.input:

"tool.execute.before": async (
  input: { tool: { name: string; input?: Record<string, unknown> }; sessionID?: string },
  output: { output?: string }
): Promise<typeof output> => {

cubic-identified Issues (Verified Resolved)

  1. Ambiguity regex false positives - ✅ Fixed in e7b95fa

    • Changed from /(maybe|perhaps|possibly|or|unclear)/i to proper word boundaries with exclusions
  2. Hardcoded dependencies_mapped - ✅ Fixed in e7b95fa

    • Now properly detects dependency mentions in prompts
  3. Test coverage for recent-history-window - ✅ Fixed in 86711df

    • Test now properly creates a loop pattern in old history

Tests

  • 18 tests passing across both hooks
  • Good BDD-style test structure with //#given, //#when, //#then comments
  • Test coverage is comprehensive for core detection logic

Integration

  • Hooks are properly registered in src/index.ts
  • Hook names added to HookNameSchema in src/config/schema.ts
  • Exports added to src/hooks/index.ts
  • Execution order is correct (loop-detector and definition-gates run after atlas)

Minor Observations

  1. Unused function argsEqual in loop-detector (line 44-52) - could be removed or used
  2. loopDetectedAt field in SessionLoopState is defined but never set
  3. The createDoDReminder function is defined but currently unused (only the inline DoD reminder string is used in the hook)

Verdict

Request Changes - The TypeScript import errors must be fixed before merge. The tests pass in isolation but the project won't compile.

Once the import errors are fixed, this is a solid feature addition with well-thought-out detection logic and proper test coverage.


Quick Fix Reference

For loop-detector/index.ts:

// Line 1 - Remove PreToolUseInput, PreToolUseOutput from import
import type { PluginInput } from "@opencode-ai/plugin"

// Lines 169-175 - Update interface
export interface LoopDetectorHook {
  "tool.execute.before": (
    input: { tool: { name: string; input?: unknown }; sessionID?: string },
    output: { output?: string }
  ) => Promise<{ output?: string }>
  event: (input: { event: { type: string; properties?: unknown } }) => Promise<void>
}

// Lines 179-182 - Update implementation signature accordingly

For definition-gates/index.ts:

// Line 1 - Remove PreToolUseInput, PreToolUseOutput from import
import type { PluginInput } from "@opencode-ai/plugin"

// Lines 157-162 - Update interface
export interface DefinitionGatesHook {
  "tool.execute.before": (
    input: { tool: { name: string; input?: unknown }; sessionID?: string },
    output: { output?: string }
  ) => Promise<{ output?: string }>
}

// Lines 166-169 - Update implementation signature accordingly

@sisyphus-dev-ai sisyphus-dev-ai removed the sisyphus: working Sisyphus is currently working on this label Jan 28, 2026
@code-yeongyu
Copy link
Owner

@agno01 Thanks for this feature! However, @sisyphus-dev-ai identified a critical issue:

TypeScript Import Errors

src/hooks/definition-gates/index.ts(1,28): error TS2305: Module '@opencode-ai/plugin' has no exported member 'PreToolUseInput'.
src/hooks/definition-gates/index.ts(1,45): error TS2305: Module '@opencode-ai/plugin' has no exported member 'PreToolUseOutput'.
src/hooks/loop-detector/index.ts(1,28): error TS2305: Module '@opencode-ai/plugin' has no exported member 'PreToolUseInput'.
src/hooks/loop-detector/index.ts(1,45): error TS2305: Module '@opencode-ai/plugin' has no exported member 'PreToolUseOutput'.

Fix: Follow the pattern used by other hooks (comment-checker, prometheus-md-only) which define inline types instead of importing non-existent types from @opencode-ai/plugin.

Please update the imports and type definitions as suggested in sisyphus-dev-ai's review above, then we can merge!

@sisyphus-dev-ai sisyphus-dev-ai added the sisyphus: working Sisyphus is currently working on this label Jan 28, 2026
@sisyphus-dev-ai
Copy link
Collaborator

TypeScript Import Errors Fixed

@agno01 I've implemented the fix for the TypeScript import errors. Since this PR is from a fork, I've pushed the fix to our repo:

Commit: c567a81

Changes Made

1. Removed invalid imports:

// Before (line 1 in both files):
import type { PluginInput, PreToolUseInput, PreToolUseOutput } from "@opencode-ai/plugin"

// After:
import type { PluginInput } from "@opencode-ai/plugin"

2. Updated type definitions to match the actual hook API:

The hooks were using input.tool.name and input.tool.input, but the actual API passes:

  • input.tool as a string (tool name directly)
  • output.args for tool arguments
// Before:
export interface LoopDetectorHook {
  "tool.execute.before": (
    input: PreToolUseInput,
    output: PreToolUseOutput
  ) => Promise<PreToolUseOutput>
}

// After:
export interface LoopDetectorHook {
  "tool.execute.before": (
    input: { tool: string; sessionID: string; callID: string },
    output: { args: Record<string, unknown>; output?: string }
  ) => Promise<{ args: Record<string, unknown>; output?: string }>
}

3. Updated implementation to use correct API:

// Before:
const toolName = input.tool.name
const toolArgs = (input.tool.input ?? {}) as Record<string, unknown>

// After:
const toolName = input.tool
const toolArgs = (output.args ?? {}) as Record<string, unknown>

Verification

  • Typecheck: PASS
  • Tests: 18/18 passing
  • Build: SUCCESS

@agno01 You have two options:

  1. Cherry-pick my commit into your branch:

    git fetch upstream
    git cherry-pick c567a81
    git push
  2. Or apply the diff manually - I can provide a patch file if needed.

Once the fix is applied, this PR should be ready to merge!

@sisyphus-dev-ai sisyphus-dev-ai removed the sisyphus: working Sisyphus is currently working on this label Jan 28, 2026
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 7 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

@agno01
Copy link
Author

agno01 commented Jan 29, 2026

@sisyphus-dev-ai cherry-picked & merged, pls recheck 👍

@code-yeongyu
Copy link
Owner

@agno01 Thanks for this valuable contribution!

The feature is approved and addresses real infinite-loop bugs. However, PR is showing CONFLICTING because dev has advanced 171 commits ahead with new hooks (stop-continuation-guard, unstable-agent-babysitter, tasks-todowrite-disabler).

Next Steps:

  1. Rebase this branch on latest dev: git rebase origin/dev
  2. Resolve any merge conflicts (should be straightforward - different hooks)
  3. Force push to update PR: git push --force-with-lease

Once rebased and CI passes, we can merge! Feel free to ask if you need help with the rebase.

@agno01 agno01 force-pushed the feat/mobius-loop-hooks branch from cdabb90 to d319557 Compare February 2, 2026 05:27
agno01 added a commit to agno01/oh-my-opencode that referenced this pull request Feb 2, 2026
- Use word boundaries for ambiguity detection to avoid false positives
  (e.g., 'factor' no longer triggers on 'or')
- Detect dependency mentions in prompt instead of hardcoding true
- Addresses cubic-dev-ai review comments on PR code-yeongyu#1188
agno01 added a commit to agno01/oh-my-opencode that referenced this pull request Feb 2, 2026
Create actual loop pattern (3 identical calls) in oldHistory to properly
test that loops outside the recent history window are not detected.
Uses varied tools in filler to avoid triggering alternating pattern.

Addresses cubic-dev-ai review on PR code-yeongyu#1188
@agno01
Copy link
Author

agno01 commented Feb 2, 2026

@code-yeongyu conflicts resolved - ready to merge! 👍

@code-yeongyu code-yeongyu added the in-review PR is currently under review label Feb 2, 2026
agno01 added a commit to agno01/oh-my-opencode that referenced this pull request Feb 2, 2026
- Use word boundaries for ambiguity detection to avoid false positives
  (e.g., 'factor' no longer triggers on 'or')
- Detect dependency mentions in prompt instead of hardcoding true
- Addresses cubic-dev-ai review comments on PR code-yeongyu#1188
agno01 added a commit to agno01/oh-my-opencode that referenced this pull request Feb 2, 2026
Create actual loop pattern (3 identical calls) in oldHistory to properly
test that loops outside the recent history window are not detected.
Uses varied tools in filler to avoid triggering alternating pattern.

Addresses cubic-dev-ai review on PR code-yeongyu#1188
@agno01 agno01 force-pushed the feat/mobius-loop-hooks branch from d319557 to 9466ab4 Compare February 2, 2026 15:44
@agno01
Copy link
Author

agno01 commented Feb 2, 2026

Issue Analysis

This PR directly addresses #1349 and may help several other open issues related to infinite loops.


Fixes #1349

[Bug]: Sisyphus-Junior stuck in infinite loop endlessly refactoring Python type hints

The loop-detector hook addresses all three patterns described in the issue:

Issue Pattern Detection Mechanism
"Code → LSP → Code" ping-pong cycle alternating_pattern - detects 6+ read-edit alternations
Repeatedly switching dictDict repeated_call - catches 3+ identical tool+args calls
Same Pyright error persisting error_loop - triggers on 2+ identical errors

The hook issues warnings with actionable recommendations ("Try a DIFFERENT approach") and escalates after 3 warnings.


May Help

#735 - Tool call JSON parse error causes infinite retry loop

Relevance: The error_loop detection (threshold: 2 same errors) would catch repeated tool calls failing with the same JSON parse error, prompting the agent to stop and fix arguments.

#78 - Agents stuck on eternal referencing of patterns

Relevance: The alternating_pattern detection may catch excessive search-read cycles when agents repeatedly clone repos looking for patterns. The 6-call threshold could trigger a "Stop and analyze why" warning.

#806 - Momus agent enters infinite loop on Windows

Relevance: General loop detection applies to any agent behavior. If Momus is cycling through the same tool calls or errors, the hook will detect and warn.


Does NOT Address

@agno01
Copy link
Author

agno01 commented Feb 2, 2026

@code-yeongyu I guess it's ready? :)

agno01 and others added 6 commits February 3, 2026 17:23
Implements loop detection and prevention based on Mobius Loop methodology.

Detects:
- Repeated identical tool calls (threshold: 3)
- Error loop patterns (same error repeated)
- Alternating tool patterns (ping-pong loops)

Addresses issues code-yeongyu#1177 and code-yeongyu#1178 where users experienced infinite loops
draining tokens.

When a loop is detected, injects a warning message prompting the agent
to stop, analyze the failure, and try a different approach.
Implements Definition of Ready and Definition of Done gates based on
Open Practice Library methodology.

Definition of Ready checks before task delegation:
- Goal is atomic (one thing)
- Files identified
- Test criteria defined
- Dependencies mapped
- No ambiguity

Definition of Done reminder when marking tasks complete:
- Tests pass
- Types pass (lsp_diagnostics clean)
- No forbidden patterns
- Follows codebase style

Helps ensure quality at task boundaries, reducing rework.
- Export new hooks from src/hooks/index.ts
- Add hook names to HookNameSchema in config
- Initialize and wire hooks in main plugin entry point
- Add to tool.execute.before and event lifecycle methods
- Use word boundaries for ambiguity detection to avoid false positives
  (e.g., 'factor' no longer triggers on 'or')
- Detect dependency mentions in prompt instead of hardcoding true
- Addresses cubic-dev-ai review comments on PR code-yeongyu#1188
Create actual loop pattern (3 identical calls) in oldHistory to properly
test that loops outside the recent history window are not detected.
Uses varied tools in filler to avoid triggering alternating pattern.

Addresses cubic-dev-ai review on PR code-yeongyu#1188
- Remove non-existent PreToolUseInput/PreToolUseOutput imports from @opencode-ai/plugin
- Use inline type definitions matching the actual hook API
- Change input.tool.name to input.tool (tool name is a string, not object)
- Change input.tool.input to output.args (tool args are in output, not input)
- Follows pattern established by comment-checker and prometheus-md-only hooks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in-review PR is currently under review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Plan mode keeps looping endlessly, unable to exit [Bug]: infinite loop

3 participants