An empty plan against an unmet goal gets one nudge, then is believed - #92

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge
Aug 5, 2026
Merged

An empty plan against an unmet goal gets one nudge, then is believed#92
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

The defect

An admitted empty proposal ends the run: the planner is saying there is no further work, and the loop believes it. That is the right default — but it held even when the operator's own goal_reached check said the run was NOT done, and the planner was never told about the contradiction. Observed with qwen3:8b against the stdlib catalog: asked for a security audit, the model returned a valid empty proposal on round 1 and the run stopped no_further_work having spent one planning call and done nothing. The goal check existed, was unsatisfied, and appeared nowhere in any prompt.

The empty-proposal branch consulted goal_reached only to choose the label (goal_met vs no_further_work), never to question the stop (loop.py, the if not proposal.nodes: branch).

The fix

One nudge, not a counter. When an admitted proposal is empty AND a goal check exists AND it is unsatisfied AND this run has not nudged before, the loop replans once with a note naming the contradiction:

Your previous proposal was empty, but the run's goal check is not yet satisfied. Propose the remaining work, or reply with an empty proposal again to confirm there is nothing more this catalog can do.

Then the planner's answer is final:

  • It proposes work → the round proceeds through the same gate as any other.
  • It repeats the empty proposalno_further_work, with the detail upgraded to say the planner confirmed it against an unsatisfied goal check.
  • Its follow-up is unusable (including a scripted planner whose reply list simply ran out) → no_further_work with "the follow-up produced nothing usable" — read as confirmation-by-silence, never counted toward max_consecutive_planning_failures. This is what keeps the stdlib scripted flow's deliberate honest-clean-stop intact (test_the_scripted_plan_runs_spend_free_and_stops_cleanly is unchanged and passes).

Runs with no goal check, or a satisfied one, behave byte-identically to before: one empty proposal, one immediate clean stop. The existing pins (test_an_admitted_empty_proposal_ends_the_run_with_no_further_work, stop-reason string set) are untouched and green.

Tests

Four new, in tests/test_planner_loop.py:

  • test_an_empty_proposal_against_an_unmet_goal_gets_one_nudge — empty then real work: run finishes goal_met, the nudge text appears in round 2's prompt, round 1 is recorded admitted-but-unexecuted.
  • test_a_second_empty_proposal_is_believed — empty twice: no_further_work after exactly 2 rounds, "confirmed no further work" in the detail, nothing executed.
  • test_an_unusable_reply_after_the_nudge_confirms_no_further_work — script exhausts after the nudge: no_further_work, not planning_failed.
  • test_an_empty_proposal_with_no_goal_check_stops_without_a_nudge — pins the unchanged default: one round, one call, clean stop.

Full suite: 1,991 passed, 12 deselected; ruff clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit 63ff75e into mainAug 5, 2026
6 checks passed
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

@Shashankss1205
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

An empty plan against an unmet goal gets one nudge, then is believed - #92

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge
Aug 5, 2026
Merged

An empty plan against an unmet goal gets one nudge, then is believed#92
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

The defect

An admitted empty proposal ends the run: the planner is saying there is no further work, and the loop believes it. That is the right default — but it held even when the operator's own goal_reached check said the run was NOT done, and the planner was never told about the contradiction. Observed with qwen3:8b against the stdlib catalog: asked for a security audit, the model returned a valid empty proposal on round 1 and the run stopped no_further_work having spent one planning call and done nothing. The goal check existed, was unsatisfied, and appeared nowhere in any prompt.

The empty-proposal branch consulted goal_reached only to choose the label (goal_met vs no_further_work), never to question the stop (loop.py, the if not proposal.nodes: branch).

The fix

One nudge, not a counter. When an admitted proposal is empty AND a goal check exists AND it is unsatisfied AND this run has not nudged before, the loop replans once with a note naming the contradiction:

Your previous proposal was empty, but the run's goal check is not yet satisfied. Propose the remaining work, or reply with an empty proposal again to confirm there is nothing more this catalog can do.

Then the planner's answer is final:

  • It proposes work → the round proceeds through the same gate as any other.
  • It repeats the empty proposalno_further_work, with the detail upgraded to say the planner confirmed it against an unsatisfied goal check.
  • Its follow-up is unusable (including a scripted planner whose reply list simply ran out) → no_further_work with "the follow-up produced nothing usable" — read as confirmation-by-silence, never counted toward max_consecutive_planning_failures. This is what keeps the stdlib scripted flow's deliberate honest-clean-stop intact (test_the_scripted_plan_runs_spend_free_and_stops_cleanly is unchanged and passes).

Runs with no goal check, or a satisfied one, behave byte-identically to before: one empty proposal, one immediate clean stop. The existing pins (test_an_admitted_empty_proposal_ends_the_run_with_no_further_work, stop-reason string set) are untouched and green.

Tests

Four new, in tests/test_planner_loop.py:

  • test_an_empty_proposal_against_an_unmet_goal_gets_one_nudge — empty then real work: run finishes goal_met, the nudge text appears in round 2's prompt, round 1 is recorded admitted-but-unexecuted.
  • test_a_second_empty_proposal_is_believed — empty twice: no_further_work after exactly 2 rounds, "confirmed no further work" in the detail, nothing executed.
  • test_an_unusable_reply_after_the_nudge_confirms_no_further_work — script exhausts after the nudge: no_further_work, not planning_failed.
  • test_an_empty_proposal_with_no_goal_check_stops_without_a_nudge — pins the unchanged default: one round, one call, clean stop.

Full suite: 1,991 passed, 12 deselected; ruff clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit 63ff75e into mainAug 5, 2026
6 checks passed
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

@Shashankss1205
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

An empty plan against an unmet goal gets one nudge, then is believed - #92

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge
Aug 5, 2026
Merged

An empty plan against an unmet goal gets one nudge, then is believed#92
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

The defect

An admitted empty proposal ends the run: the planner is saying there is no further work, and the loop believes it. That is the right default — but it held even when the operator's own goal_reached check said the run was NOT done, and the planner was never told about the contradiction. Observed with qwen3:8b against the stdlib catalog: asked for a security audit, the model returned a valid empty proposal on round 1 and the run stopped no_further_work having spent one planning call and done nothing. The goal check existed, was unsatisfied, and appeared nowhere in any prompt.

The empty-proposal branch consulted goal_reached only to choose the label (goal_met vs no_further_work), never to question the stop (loop.py, the if not proposal.nodes: branch).

The fix

One nudge, not a counter. When an admitted proposal is empty AND a goal check exists AND it is unsatisfied AND this run has not nudged before, the loop replans once with a note naming the contradiction:

Your previous proposal was empty, but the run's goal check is not yet satisfied. Propose the remaining work, or reply with an empty proposal again to confirm there is nothing more this catalog can do.

Then the planner's answer is final:

  • It proposes work → the round proceeds through the same gate as any other.
  • It repeats the empty proposalno_further_work, with the detail upgraded to say the planner confirmed it against an unsatisfied goal check.
  • Its follow-up is unusable (including a scripted planner whose reply list simply ran out) → no_further_work with "the follow-up produced nothing usable" — read as confirmation-by-silence, never counted toward max_consecutive_planning_failures. This is what keeps the stdlib scripted flow's deliberate honest-clean-stop intact (test_the_scripted_plan_runs_spend_free_and_stops_cleanly is unchanged and passes).

Runs with no goal check, or a satisfied one, behave byte-identically to before: one empty proposal, one immediate clean stop. The existing pins (test_an_admitted_empty_proposal_ends_the_run_with_no_further_work, stop-reason string set) are untouched and green.

Tests

Four new, in tests/test_planner_loop.py:

  • test_an_empty_proposal_against_an_unmet_goal_gets_one_nudge — empty then real work: run finishes goal_met, the nudge text appears in round 2's prompt, round 1 is recorded admitted-but-unexecuted.
  • test_a_second_empty_proposal_is_believed — empty twice: no_further_work after exactly 2 rounds, "confirmed no further work" in the detail, nothing executed.
  • test_an_unusable_reply_after_the_nudge_confirms_no_further_work — script exhausts after the nudge: no_further_work, not planning_failed.
  • test_an_empty_proposal_with_no_goal_check_stops_without_a_nudge — pins the unchanged default: one round, one call, clean stop.

Full suite: 1,991 passed, 12 deselected; ruff clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit 63ff75e into mainAug 5, 2026
6 checks passed
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

@Shashankss1205
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

An empty plan against an unmet goal gets one nudge, then is believed - #92

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge
Aug 5, 2026
Merged

An empty plan against an unmet goal gets one nudge, then is believed#92
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

The defect

An admitted empty proposal ends the run: the planner is saying there is no further work, and the loop believes it. That is the right default — but it held even when the operator's own goal_reached check said the run was NOT done, and the planner was never told about the contradiction. Observed with qwen3:8b against the stdlib catalog: asked for a security audit, the model returned a valid empty proposal on round 1 and the run stopped no_further_work having spent one planning call and done nothing. The goal check existed, was unsatisfied, and appeared nowhere in any prompt.

The empty-proposal branch consulted goal_reached only to choose the label (goal_met vs no_further_work), never to question the stop (loop.py, the if not proposal.nodes: branch).

The fix

One nudge, not a counter. When an admitted proposal is empty AND a goal check exists AND it is unsatisfied AND this run has not nudged before, the loop replans once with a note naming the contradiction:

Your previous proposal was empty, but the run's goal check is not yet satisfied. Propose the remaining work, or reply with an empty proposal again to confirm there is nothing more this catalog can do.

Then the planner's answer is final:

  • It proposes work → the round proceeds through the same gate as any other.
  • It repeats the empty proposalno_further_work, with the detail upgraded to say the planner confirmed it against an unsatisfied goal check.
  • Its follow-up is unusable (including a scripted planner whose reply list simply ran out) → no_further_work with "the follow-up produced nothing usable" — read as confirmation-by-silence, never counted toward max_consecutive_planning_failures. This is what keeps the stdlib scripted flow's deliberate honest-clean-stop intact (test_the_scripted_plan_runs_spend_free_and_stops_cleanly is unchanged and passes).

Runs with no goal check, or a satisfied one, behave byte-identically to before: one empty proposal, one immediate clean stop. The existing pins (test_an_admitted_empty_proposal_ends_the_run_with_no_further_work, stop-reason string set) are untouched and green.

Tests

Four new, in tests/test_planner_loop.py:

  • test_an_empty_proposal_against_an_unmet_goal_gets_one_nudge — empty then real work: run finishes goal_met, the nudge text appears in round 2's prompt, round 1 is recorded admitted-but-unexecuted.
  • test_a_second_empty_proposal_is_believed — empty twice: no_further_work after exactly 2 rounds, "confirmed no further work" in the detail, nothing executed.
  • test_an_unusable_reply_after_the_nudge_confirms_no_further_work — script exhausts after the nudge: no_further_work, not planning_failed.
  • test_an_empty_proposal_with_no_goal_check_stops_without_a_nudge — pins the unchanged default: one round, one call, clean stop.

Full suite: 1,991 passed, 12 deselected; ruff clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit 63ff75e into mainAug 5, 2026
6 checks passed
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

@Shashankss1205
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

An empty plan against an unmet goal gets one nudge, then is believed - #92

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge
Aug 5, 2026
Merged

An empty plan against an unmet goal gets one nudge, then is believed#92
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

The defect

An admitted empty proposal ends the run: the planner is saying there is no further work, and the loop believes it. That is the right default — but it held even when the operator's own goal_reached check said the run was NOT done, and the planner was never told about the contradiction. Observed with qwen3:8b against the stdlib catalog: asked for a security audit, the model returned a valid empty proposal on round 1 and the run stopped no_further_work having spent one planning call and done nothing. The goal check existed, was unsatisfied, and appeared nowhere in any prompt.

The empty-proposal branch consulted goal_reached only to choose the label (goal_met vs no_further_work), never to question the stop (loop.py, the if not proposal.nodes: branch).

The fix

One nudge, not a counter. When an admitted proposal is empty AND a goal check exists AND it is unsatisfied AND this run has not nudged before, the loop replans once with a note naming the contradiction:

Your previous proposal was empty, but the run's goal check is not yet satisfied. Propose the remaining work, or reply with an empty proposal again to confirm there is nothing more this catalog can do.

Then the planner's answer is final:

  • It proposes work → the round proceeds through the same gate as any other.
  • It repeats the empty proposalno_further_work, with the detail upgraded to say the planner confirmed it against an unsatisfied goal check.
  • Its follow-up is unusable (including a scripted planner whose reply list simply ran out) → no_further_work with "the follow-up produced nothing usable" — read as confirmation-by-silence, never counted toward max_consecutive_planning_failures. This is what keeps the stdlib scripted flow's deliberate honest-clean-stop intact (test_the_scripted_plan_runs_spend_free_and_stops_cleanly is unchanged and passes).

Runs with no goal check, or a satisfied one, behave byte-identically to before: one empty proposal, one immediate clean stop. The existing pins (test_an_admitted_empty_proposal_ends_the_run_with_no_further_work, stop-reason string set) are untouched and green.

Tests

Four new, in tests/test_planner_loop.py:

  • test_an_empty_proposal_against_an_unmet_goal_gets_one_nudge — empty then real work: run finishes goal_met, the nudge text appears in round 2's prompt, round 1 is recorded admitted-but-unexecuted.
  • test_a_second_empty_proposal_is_believed — empty twice: no_further_work after exactly 2 rounds, "confirmed no further work" in the detail, nothing executed.
  • test_an_unusable_reply_after_the_nudge_confirms_no_further_work — script exhausts after the nudge: no_further_work, not planning_failed.
  • test_an_empty_proposal_with_no_goal_check_stops_without_a_nudge — pins the unchanged default: one round, one call, clean stop.

Full suite: 1,991 passed, 12 deselected; ruff clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit 63ff75e into mainAug 5, 2026
6 checks passed
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

@Shashankss1205
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

An empty plan against an unmet goal gets one nudge, then is believed - #92

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge
Aug 5, 2026
Merged

An empty plan against an unmet goal gets one nudge, then is believed#92
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

The defect

An admitted empty proposal ends the run: the planner is saying there is no further work, and the loop believes it. That is the right default — but it held even when the operator's own goal_reached check said the run was NOT done, and the planner was never told about the contradiction. Observed with qwen3:8b against the stdlib catalog: asked for a security audit, the model returned a valid empty proposal on round 1 and the run stopped no_further_work having spent one planning call and done nothing. The goal check existed, was unsatisfied, and appeared nowhere in any prompt.

The empty-proposal branch consulted goal_reached only to choose the label (goal_met vs no_further_work), never to question the stop (loop.py, the if not proposal.nodes: branch).

The fix

One nudge, not a counter. When an admitted proposal is empty AND a goal check exists AND it is unsatisfied AND this run has not nudged before, the loop replans once with a note naming the contradiction:

Your previous proposal was empty, but the run's goal check is not yet satisfied. Propose the remaining work, or reply with an empty proposal again to confirm there is nothing more this catalog can do.

Then the planner's answer is final:

  • It proposes work → the round proceeds through the same gate as any other.
  • It repeats the empty proposalno_further_work, with the detail upgraded to say the planner confirmed it against an unsatisfied goal check.
  • Its follow-up is unusable (including a scripted planner whose reply list simply ran out) → no_further_work with "the follow-up produced nothing usable" — read as confirmation-by-silence, never counted toward max_consecutive_planning_failures. This is what keeps the stdlib scripted flow's deliberate honest-clean-stop intact (test_the_scripted_plan_runs_spend_free_and_stops_cleanly is unchanged and passes).

Runs with no goal check, or a satisfied one, behave byte-identically to before: one empty proposal, one immediate clean stop. The existing pins (test_an_admitted_empty_proposal_ends_the_run_with_no_further_work, stop-reason string set) are untouched and green.

Tests

Four new, in tests/test_planner_loop.py:

  • test_an_empty_proposal_against_an_unmet_goal_gets_one_nudge — empty then real work: run finishes goal_met, the nudge text appears in round 2's prompt, round 1 is recorded admitted-but-unexecuted.
  • test_a_second_empty_proposal_is_believed — empty twice: no_further_work after exactly 2 rounds, "confirmed no further work" in the detail, nothing executed.
  • test_an_unusable_reply_after_the_nudge_confirms_no_further_work — script exhausts after the nudge: no_further_work, not planning_failed.
  • test_an_empty_proposal_with_no_goal_check_stops_without_a_nudge — pins the unchanged default: one round, one call, clean stop.

Full suite: 1,991 passed, 12 deselected; ruff clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit 63ff75e into mainAug 5, 2026
6 checks passed
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

@Shashankss1205
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

An empty plan against an unmet goal gets one nudge, then is believed - #92

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge
Aug 5, 2026
Merged

An empty plan against an unmet goal gets one nudge, then is believed#92
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

The defect

An admitted empty proposal ends the run: the planner is saying there is no further work, and the loop believes it. That is the right default — but it held even when the operator's own goal_reached check said the run was NOT done, and the planner was never told about the contradiction. Observed with qwen3:8b against the stdlib catalog: asked for a security audit, the model returned a valid empty proposal on round 1 and the run stopped no_further_work having spent one planning call and done nothing. The goal check existed, was unsatisfied, and appeared nowhere in any prompt.

The empty-proposal branch consulted goal_reached only to choose the label (goal_met vs no_further_work), never to question the stop (loop.py, the if not proposal.nodes: branch).

The fix

One nudge, not a counter. When an admitted proposal is empty AND a goal check exists AND it is unsatisfied AND this run has not nudged before, the loop replans once with a note naming the contradiction:

Your previous proposal was empty, but the run's goal check is not yet satisfied. Propose the remaining work, or reply with an empty proposal again to confirm there is nothing more this catalog can do.

Then the planner's answer is final:

  • It proposes work → the round proceeds through the same gate as any other.
  • It repeats the empty proposalno_further_work, with the detail upgraded to say the planner confirmed it against an unsatisfied goal check.
  • Its follow-up is unusable (including a scripted planner whose reply list simply ran out) → no_further_work with "the follow-up produced nothing usable" — read as confirmation-by-silence, never counted toward max_consecutive_planning_failures. This is what keeps the stdlib scripted flow's deliberate honest-clean-stop intact (test_the_scripted_plan_runs_spend_free_and_stops_cleanly is unchanged and passes).

Runs with no goal check, or a satisfied one, behave byte-identically to before: one empty proposal, one immediate clean stop. The existing pins (test_an_admitted_empty_proposal_ends_the_run_with_no_further_work, stop-reason string set) are untouched and green.

Tests

Four new, in tests/test_planner_loop.py:

  • test_an_empty_proposal_against_an_unmet_goal_gets_one_nudge — empty then real work: run finishes goal_met, the nudge text appears in round 2's prompt, round 1 is recorded admitted-but-unexecuted.
  • test_a_second_empty_proposal_is_believed — empty twice: no_further_work after exactly 2 rounds, "confirmed no further work" in the detail, nothing executed.
  • test_an_unusable_reply_after_the_nudge_confirms_no_further_work — script exhausts after the nudge: no_further_work, not planning_failed.
  • test_an_empty_proposal_with_no_goal_check_stops_without_a_nudge — pins the unchanged default: one round, one call, clean stop.

Full suite: 1,991 passed, 12 deselected; ruff clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit 63ff75e into mainAug 5, 2026
6 checks passed
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

@Shashankss1205
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

An empty plan against an unmet goal gets one nudge, then is believed - #92

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge
Aug 5, 2026
Merged

An empty plan against an unmet goal gets one nudge, then is believed#92
Shashankss1205 merged 1 commit into
mainfrom
fix/empty-proposal-nudge

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

The defect

An admitted empty proposal ends the run: the planner is saying there is no further work, and the loop believes it. That is the right default — but it held even when the operator's own goal_reached check said the run was NOT done, and the planner was never told about the contradiction. Observed with qwen3:8b against the stdlib catalog: asked for a security audit, the model returned a valid empty proposal on round 1 and the run stopped no_further_work having spent one planning call and done nothing. The goal check existed, was unsatisfied, and appeared nowhere in any prompt.

The empty-proposal branch consulted goal_reached only to choose the label (goal_met vs no_further_work), never to question the stop (loop.py, the if not proposal.nodes: branch).

The fix

One nudge, not a counter. When an admitted proposal is empty AND a goal check exists AND it is unsatisfied AND this run has not nudged before, the loop replans once with a note naming the contradiction:

Your previous proposal was empty, but the run's goal check is not yet satisfied. Propose the remaining work, or reply with an empty proposal again to confirm there is nothing more this catalog can do.

Then the planner's answer is final:

  • It proposes work → the round proceeds through the same gate as any other.
  • It repeats the empty proposalno_further_work, with the detail upgraded to say the planner confirmed it against an unsatisfied goal check.
  • Its follow-up is unusable (including a scripted planner whose reply list simply ran out) → no_further_work with "the follow-up produced nothing usable" — read as confirmation-by-silence, never counted toward max_consecutive_planning_failures. This is what keeps the stdlib scripted flow's deliberate honest-clean-stop intact (test_the_scripted_plan_runs_spend_free_and_stops_cleanly is unchanged and passes).

Runs with no goal check, or a satisfied one, behave byte-identically to before: one empty proposal, one immediate clean stop. The existing pins (test_an_admitted_empty_proposal_ends_the_run_with_no_further_work, stop-reason string set) are untouched and green.

Tests

Four new, in tests/test_planner_loop.py:

  • test_an_empty_proposal_against_an_unmet_goal_gets_one_nudge — empty then real work: run finishes goal_met, the nudge text appears in round 2's prompt, round 1 is recorded admitted-but-unexecuted.
  • test_a_second_empty_proposal_is_believed — empty twice: no_further_work after exactly 2 rounds, "confirmed no further work" in the detail, nothing executed.
  • test_an_unusable_reply_after_the_nudge_confirms_no_further_work — script exhausts after the nudge: no_further_work, not planning_failed.
  • test_an_empty_proposal_with_no_goal_check_stops_without_a_nudge — pins the unchanged default: one round, one call, clean stop.

Full suite: 1,991 passed, 12 deselected; ruff clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit 63ff75e into mainAug 5, 2026
6 checks passed
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

@Shashankss1205