{"id":22493,"date":"2026-07-20T21:29:01","date_gmt":"2026-07-20T21:29:01","guid":{"rendered":"https:\/\/scannn.com\/fable-5-vs-gpt-5-6-sol-on-an-np-hard-problem-does-goal-help\/"},"modified":"2026-07-20T21:29:01","modified_gmt":"2026-07-20T21:29:01","slug":"fable-5-vs-gpt-5-6-sol-on-an-np-hard-problem-does-goal-help","status":"publish","type":"post","link":"https:\/\/scannn.com\/lv\/fable-5-vs-gpt-5-6-sol-on-an-np-hard-problem-does-goal-help\/","title":{"rendered":"Fable 5 vs. GPT-5.6 Sol on an NP-Hard Problem: Does \/goal Help?"},"content":{"rendered":"\n<div>\n<p><strong>TL;DR:<\/strong> I gave Claude Fable 5 and GPT-5.6 Sol the same unpublished NP-hard<br \/>\noptimization problem, with and without their native <code>\/goal<\/code> mode. Fable 5 is an absolute beast;<br \/>\n<code>\/goal<\/code> is not a game changer.<\/p>\n<p><\/p>\n<p><strong>Context:<\/strong> This is an operations research problem originally submitted to students at a<br \/>\nhackathon. I spent a week years<br \/>\nago writing C++ to solve it, so I have a useful human baseline.<\/p>\n<p>Fable 5 was an absolute beast on this benchmark. It produced the best solution overall, and its<br \/>\nconsistency is unlike anything I have seen from a model on this problem. This is pure raw<br \/>\nintelligence. Incredible.<\/p>\n<p>The other result is that <code>\/goal<\/code> is not a generic \u201ctry harder\u201d switch. It changes the control<br \/>\nloop and the search path. Sometimes that finds a better basin. Sometimes it gives a bad idea<br \/>\nmore time to mature.<\/p>\n<p>All code, prompts, result tables, exclusions, and trajectory notes are in<br \/>\n<a href=\"https:\/\/github.com\/charles-azam\/CLIArena\">CLIArena<\/a>. This is a follow-up to my<br \/>\n<a href=\"https:\/\/charlesazam.com\/blog\/kiro-benchmark\/\">first article about this benchmark<\/a>.<\/p>\n<hr\/>\n<h2 id=\"the-problem\">The problem<\/h2>\n<p>KIRO is a fiber-network design problem I worked on as an engineering student in 2018. Given<br \/>\ndirected distance matrices for Grenoble, Nice, and Paris, the solver has to connect distribution<br \/>\npoints and terminals using loops and short chains while respecting several structural<br \/>\nconstraints. The objective is total cable length. Lower is better.<\/p>\n<p><img loading=\"lazy\" alt=\"KIRO network structure: a directed loop rooted at a hub with a short branch\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"auto\" width=\"2365\" height=\"1234\" src=\"https:\/\/charlesazam.com\/_astro\/fig0_kiro_problem.CYvcjsCU_Z1X0vi6.webp\"\/><\/p>\n<p>A valid network consists of redundant loops rooted at distribution hubs, with short branches<br \/>\nhanging from towers on those loops. Every tower must appear exactly once, and reversing a cable<br \/>\nsegment can change its cost.<\/p>\n<h3 id=\"how-large-is-the-search-space\">How large is the search space?<\/h3>\n<p>There is no single closed-form count because a solution can use any number of loops, variable<br \/>\nloop sizes, and differently anchored and ordered branches. But Paris alone gives a useful lower<br \/>\nbound.<\/p>\n<p>Even if we ignore ordering and branches and only assign each of the 532 terminals to one of<br \/>\n11 distribution hubs, there are <code>11^532<\/code> possible assignments.<\/p>\n<p>A stronger lower bound comes from one deliberately restricted family of valid solutions:<br \/>\nexactly 19 loops of 28 terminals each, with no branches. This covers all 532 terminals because<br \/>\n<code>19 x 28 = 532<\/code>, while staying below the 30-terminal limit for a loop. Order the 532 terminals,<br \/>\nsplit that ordering into 19 consecutive groups, divide by <code>19!<\/code> because the set of loops is<br \/>\nunordered, and choose one of the 11 hubs for each loop:<\/p>\n<p><code>(532! \/ 19!) x 11^19 ~= 10^1223<\/code><\/p>\n<h2 id=\"what-i-tested\">What I tested<\/h2>\n<p>The primary experiment was intentionally narrow:<\/p>\n<table>\n<thead>\n<tr>\n<th>Setting<\/th>\n<th>Value<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Models<\/td>\n<td>Claude Fable 5, Opus 4.8, Sonnet 5; GPT-5.6 Sol, Terra, Luna<\/td>\n<\/tr>\n<tr>\n<td>Modes<\/td>\n<td>Plain; native <code>\/goal<\/code><\/td>\n<\/tr>\n<tr>\n<td>Optimization budget<\/td>\n<td>30 minutes<\/td>\n<\/tr>\n<tr>\n<td>Outer agent timeout<\/td>\n<td>1,900 seconds<\/td>\n<\/tr>\n<tr>\n<td>Reasoning<\/td>\n<td>Maximum available setting for every model<\/td>\n<\/tr>\n<tr>\n<td>Execution<\/td>\n<td>Harbor 0.1.43, Docker, subscription authentication<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"results\">Results<\/h2>\n<p>Before concentrating repetitions on the flagship pair, I ran one matched 30-minute no-hint<br \/>\npair for every model in the sweep. For Fable and Sol, the chart uses Pair 1 from the replicated<br \/>\nheadline set; the other four models have one pair each.<\/p>\n<p><img loading=\"lazy\" alt=\"All six models in one matched 30-minute no-hint pair\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"auto\" width=\"2294\" height=\"1244\" src=\"https:\/\/charlesazam.com\/_astro\/fig6_all_models_30min.Cp2VuCKx_Z2jLU4Y.webp\"\/><\/p>\n<p>I then repeated the flagship comparison until I had three matched runs for Fable 5 and three<br \/>\nfor Sol.<\/p>\n<p><img loading=\"lazy\" alt=\"Clean score distributions\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"auto\" width=\"1789\" height=\"1164\" src=\"https:\/\/charlesazam.com\/_astro\/fig3_clean_distributions.Ct-GZQ3W_2lndA9.webp\"\/><\/p>\n<table>\n<thead>\n<tr>\n<th>Model<\/th>\n<th align=\"right\">Run<\/th>\n<th align=\"right\">Plain<\/th>\n<th align=\"right\"><code>\/goal<\/code><\/th>\n<th align=\"right\">Goal minus plain<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Fable 5<\/td>\n<td align=\"right\">1<\/td>\n<td align=\"right\">32,197<\/td>\n<td align=\"right\"><strong>31,934<\/strong><\/td>\n<td align=\"right\">-263<\/td>\n<\/tr>\n<tr>\n<td>Fable 5<\/td>\n<td align=\"right\">2<\/td>\n<td align=\"right\">32,516<\/td>\n<td align=\"right\"><strong>32,324<\/strong><\/td>\n<td align=\"right\">-192<\/td>\n<\/tr>\n<tr>\n<td>Fable 5<\/td>\n<td align=\"right\">3<\/td>\n<td align=\"right\"><strong>32,446<\/strong><\/td>\n<td align=\"right\">35,178<\/td>\n<td align=\"right\">+2,732<\/td>\n<\/tr>\n<tr>\n<td>GPT-5.6 Sol<\/td>\n<td align=\"right\">1<\/td>\n<td align=\"right\"><strong>33,581<\/strong><\/td>\n<td align=\"right\">39,371<\/td>\n<td align=\"right\">+5,790<\/td>\n<\/tr>\n<tr>\n<td>GPT-5.6 Sol<\/td>\n<td align=\"right\">2<\/td>\n<td align=\"right\">35,539<\/td>\n<td align=\"right\"><strong>32,703<\/strong><\/td>\n<td align=\"right\">-2,836<\/td>\n<\/tr>\n<tr>\n<td>GPT-5.6 Sol<\/td>\n<td align=\"right\">3<\/td>\n<td align=\"right\">33,663<\/td>\n<td align=\"right\"><strong>33,313<\/strong><\/td>\n<td align=\"right\">-350<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Negative means <code>\/goal<\/code> was better. Goal won four of six trials, so win rate alone makes the<br \/>\nfeature look useful. The means tell the other half:<\/p>\n<table>\n<thead>\n<tr>\n<th>Model<\/th>\n<th align=\"right\">Plain mean<\/th>\n<th align=\"right\"><code>\/goal<\/code> mean<\/th>\n<th align=\"right\">Mean effect<\/th>\n<th align=\"right\">Median effect<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Fable 5<\/td>\n<td align=\"right\"><strong>32,386<\/strong><\/td>\n<td align=\"right\">33,145<\/td>\n<td align=\"right\">+759 worse<\/td>\n<td align=\"right\">-192 better<\/td>\n<\/tr>\n<tr>\n<td>GPT-5.6 Sol<\/td>\n<td align=\"right\"><strong>34,261<\/strong><\/td>\n<td align=\"right\">35,129<\/td>\n<td align=\"right\">+868 worse<\/td>\n<td align=\"right\">-350 better<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Both models usually got a small benefit and occasionally suffered a large regression. That is<br \/>\nwhy <code>\/goal<\/code> won most runs but made both means worse.<\/p>\n<p>Fable was also clearly stronger. Its plain mean beat Sol\u2019s by 1,875 points, and its goal mean<br \/>\nbeat Sol\u2019s by 1,984. More importantly, Fable plain stayed inside a tiny 319-point range while<br \/>\nSol plain spanned 1,958 points. Fable goal produced the best clean score, 31,934; Fable plain<br \/>\nwas the safest configuration.<\/p>\n<h2 id=\"deep-dive-into-the-goal-command\">Deep dive into the goal command<\/h2>\n<h2 id=\"the-same-command-hides-two-different-systems\">The same command hides two different systems<\/h2>\n<p>Claude Code and Codex both expose <code>\/goal<\/code>, but the implementations are fundamentally different.<\/p>\n<p><img loading=\"lazy\" alt=\"Claude Code and Codex goal architecture\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"auto\" width=\"2574\" height=\"1421\" src=\"https:\/\/charlesazam.com\/_astro\/fig4_goal_architecture.CUL-v7ck_ZQOPcm.webp\"\/><\/p>\n<h3 id=\"claude-code-a-separate-evaluator\">Claude Code: a separate evaluator<\/h3>\n<p>Claude Code implements <code>\/goal<\/code> as a session-scoped Stop hook. After each main-model turn, a<br \/>\nsmall evaluator model, Haiku by default, reads the condition and conversation. It returns yes<br \/>\nor no with a reason. A no starts another turn; a yes clears the goal.<\/p>\n<p>The evaluator cannot use tools or inspect files. It can only judge evidence that appeared in<br \/>\nthe transcript. That can catch an early exit, but it cannot know whether another ten million<br \/>\nsolver iterations are worthwhile. <a href=\"https:\/\/code.claude.com\/docs\/en\/goal\">Anthropic\u2019s goal documentation<\/a><\/p>\n<p>Keep in mind that claude code is not open source, so we rely solely on what Anthropic tells us.<\/p>\n<h3 id=\"codex-persisted-state-and-lifecycle-tools\">Codex: persisted state and lifecycle tools<\/h3>\n<p>I also read the source for the benchmarked release,<br \/>\n<a href=\"https:\/\/github.com\/openai\/codex\/tree\/rust-v0.144.4\">Codex CLI 0.144.4<\/a>. Codex treats a goal as<br \/>\npersisted thread state:<\/p>\n<ol>\n<li>The TUI saves the objective for the active thread, and SQLite stores its status and budget<br \/>\naccounting. <a href=\"https:\/\/github.com\/openai\/codex\/blob\/rust-v0.144.4\/codex-rs\/tui\/src\/app\/thread_goal_actions.rs#L128-L227\">TUI<\/a>,<br \/>\n<a href=\"https:\/\/github.com\/openai\/codex\/blob\/rust-v0.144.4\/codex-rs\/state\/goals_migrations\/0001_thread_goals.sql\">schema<\/a><\/li>\n<li>The working model receives <code>create_goal<\/code>, <code>get_goal<\/code>, and <code>update_goal<\/code> tools.<br \/>\n<a href=\"https:\/\/github.com\/openai\/codex\/blob\/rust-v0.144.4\/codex-rs\/ext\/goal\/src\/spec.rs\">Tool specification<\/a><\/li>\n<li>If the thread becomes idle while the goal is active, Codex injects a continuation turn with<br \/>\nthe objective and a completion audit. <a href=\"https:\/\/github.com\/openai\/codex\/blob\/rust-v0.144.4\/codex-rs\/ext\/goal\/src\/runtime.rs#L359-L414\">Runtime<\/a>,<br \/>\n<a href=\"https:\/\/github.com\/openai\/codex\/blob\/rust-v0.144.4\/codex-rs\/ext\/goal\/templates\/goals\/continuation.md\">prompt<\/a><\/li>\n<\/ol>\n<p>Claude delegates completion to another model. Codex lets the working model declare completion,<br \/>\nthen resumes it while the persisted goal remains active. Claude\u2019s evaluator is independent but<br \/>\nsees only the transcript; Codex sees the files and tools but effectively grades its own work.<\/p>\n<h2 id=\"why-goal-can-win-most-runs-and-still-be-a-bad-default\">Why <code>\/goal<\/code> can win most runs and still be a bad default<\/h2>\n<p>On a normal coding task, progress is often legible: another turn can fix a test or complete a<br \/>\nmigration. Optimization is different. Once an agent chooses a solver, extra time can amplify<br \/>\neither a good decision or a bad one.<\/p>\n<p>That is exactly what happened here. Goal helped when it sustained Fable\u2019s fast compiled<br \/>\nportfolio or Sol\u2019s successful chain repartition. It hurt when Fable built a slow solver or Sol<br \/>\ncommitted to an exhaustive anchor sweep. The median moved slightly in the right direction; the<br \/>\nbad tail moved much farther in the wrong one.<\/p>\n<h2 id=\"limitations\">Limitations<\/h2>\n<p>This is one unpublished NP-hard task, not a general coding leaderboard. Only Fable and Sol have<br \/>\nthree clean matched pairs. Other comparisons mix prompts, wrapper versions, and time limits,<br \/>\nand the trials ran sequentially through subscription services that may have drifted.<\/p>\n<p>The containers exposed eight CPUs despite task metadata declaring one, which favored Fable\u2019s<br \/>\nparallel portfolios. Every scored Fable and Sol output was valid, partly because the wrapper<br \/>\nrequired early checkpoints and final verification. The benchmark measures the complete system:<br \/>\nmodel, CLI, prompt, subscription service, and harness.<\/p>\n<h2 id=\"reproducing-this\">Reproducing this<\/h2>\n<p>The benchmark task, wrappers, analysis scripts, figure generator, and full evidence memo are in<br \/>\n<a href=\"https:\/\/github.com\/charles-azam\/CLIArena\">CLIArena<\/a>. Raw job directories are excluded from Git<br \/>\nbecause of their size, but the memo records every publishable score, city breakdown, elapsed<br \/>\ntime, strategy, exclusion, and run ID.<\/p>\n<p>The primary commands are:<\/p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"color:#E1E4E8\">RUN_ID<\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#9ECBFF\">article-kiro-YYYYMMDD-clean<\/span><span style=\"color:#B392F0\"> \\<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">PHASE=nohint-all <\/span><span style=\"color:#79B8FF\">\\<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">.\/scripts\/run_subscription_article_matrix.sh<\/span><\/span>\n<span class=\"line\"\/>\n<span class=\"line\"><span style=\"color:#B392F0\">uv<\/span><span style=\"color:#9ECBFF\"> run<\/span><span style=\"color:#9ECBFF\"> python<\/span><span style=\"color:#9ECBFF\"> scripts\/summarize_subscription_article_results.py<\/span><span style=\"color:#9ECBFF\"> RUN_ID...<\/span><\/span>\n<span class=\"line\"><span style=\"color:#B392F0\">uv<\/span><span style=\"color:#9ECBFF\"> run<\/span><span style=\"color:#9ECBFF\"> python<\/span><span style=\"color:#9ECBFF\"> scripts\/analyze_subscription_article_results.py<\/span><span style=\"color:#9ECBFF\"> RUN_ID...<\/span><\/span><\/code><\/pre>\n<p>The result I would put in the headline is not that goal helps or hurts. It is that a persistence<br \/>\nfeature can win most individual trials while making observed average performance worse. On a hard<br \/>\noptimization problem, the quality of the loop matters less than the quality of what the loop<br \/>\nkeeps doing.<\/p>\n<\/p><\/div>\n<p><a href=\"https:\/\/charlesazam.com\/blog\/fable-5-gpt-5-6-sol-goal\/?utm_source=tldrai\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>TL;DR: I gave Claude Fable 5 and GPT-5.6 Sol the same unpublished NP-hard optimization problem, with and without their native \/goal mode. Fable 5 is an absolute beast; \/goal is not a game changer. Context: This is an operations research problem originally submitted to students at a hackathon. I spent a week years ago writing [&hellip;]<\/p>\n","protected":false},"author":16,"featured_media":22494,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[143],"tags":[],"class_list":["post-22493","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai"],"_links":{"self":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/22493","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/comments?post=22493"}],"version-history":[{"count":0,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/22493\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media\/22494"}],"wp:attachment":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media?parent=22493"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/categories?post=22493"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/tags?post=22493"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}