fix: compaction indicator flicker + reasoning-fuel surfacing#96
Merged
Conversation
…port parsing Introduce the plan's living logical world: a persistent registry of model-asserted VRF statements compiled into a multi-domain program the engine re-checks as a whole. Statements accumulate across stages, carry file anchors that demote FACT→BELIEVES when the source changes, and the directory always mirrors the registry. Crucially, the store treats the engine exactly as planner_elenchus_check does: it runs a program, scans the output for one of four verdict codes, and hands the raw output to callers verbatim. It never parses the report body — no orphans, beliefs, or derivations — so pi-planner grows no dependency on the engine's private JSON shape for model-authored programs. A source-level invariant test guards this: the module must not reference any report internals. Everything a fuel/directive layer needs beyond the verdict comes from the planner's own artifacts and its own anchor hash sweep, never from the engine's output. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eb, zero engine parsing
Add the deterministic fuel scalar and its collectors. Fuel measures how much
of the interacting-condition web on the table the model actually ran through
the engine, and nudges only where a web exists:
unmet = max(0, warrantedWeb - coverage)
fuel = round(100 * (1 - (unmet+stale+friction) / (warrantedWeb+stale+friction+1)))
= null when there is no web, no stale anchor, and no friction
This yields the intended 2×2: skipping the engine where a web exists depletes
fuel in proportion to the ignored web (anti-laziness); running it where there
is none earns nothing and costs nothing (anti-ritual, no punishment). Coverage
is capped at the warranted web so a no-web run cannot manufacture credit.
Every input comes from the planner's own artifacts and records — branches from
the behavior board, constraints from the spec, shared surfaces from the task
graph, the verdict enum + gate-repeat counter from last-check, staleness from
the compiler's own anchor sweep. Nothing reads the engine's report. Engagement
credits only model-authored runs (a mechanical gate is the floor, not the
model's reasoning) and credits a CONFLICT as honest engagement — the existing
CONFLICT-block, not fuel, is what stops the step from advancing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…urface fuel, never parse the engine
Add the planner_reason wrapper tool with three modes over the slim world store:
assert — add statements to a domain and re-check the whole world; an
observation may name a source file to anchor to (hashed here).
retract — remove statements by id and re-check (the anti-deadlock escape,
always applied; unknown ids are reported, never thrown).
recheck — re-run the world as-is.
Every response returns the verdict code and the engine's raw output verbatim —
the tool parses nothing — plus the step's reasoning-fuel directive. Fuel is
assembled by reason-context from the planner's own artifacts (spec constraints
at consistency_check, task branches at the execution reasoning steps, shared
task surfaces at doubt_review) and rendered by reason-directive's tone ladder:
silent when no web is warranted, quiet at ≥70, naming the deficit below that.
The run writes a model-authored last-check record so fuel credits the
engagement; a CONFLICT still hard-blocks finish_step via the existing guard.
Gated exactly like planner_elenchus_check: registered in PLANNER_WRAPPER_TOOLS,
listed at the six reasoning steps in both tool-policy and stage-behavior, and
scoped into the broken-state repair_or_resume set. Dual-gate invariant and
tool-count tests updated accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…er_status Two pieces that surface fuel where the model reads it: - writeElenchusLastCheck now carries a `repeat` counter: same gate + same sourceHash + same verdict in a row increments it; any real change (new source) or a different verdict resets it; a model-authored check never accrues one. This is the sole source of the gate-thrash friction signal — derived from the planner's own record, no new journal. - planner_status renders a "## Reasoning Fuel" section at the reasoning steps, computed by the shared reason-context loader and rendered by the tone ladder: silent when no web is warranted, a single line at ≥70, the top deficit and a cheap move in 30–69, and the directing "what's needed / not needed" pair below 30 or on any friction. Fixed the ladder so any friction forces the directing tone even at otherwise-high fuel — a named pathology must surface. Fuel remains tone-only: it enters no allow/block condition anywhere. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ecision Enforce the load-bearing property structurally: the seven modules that decide what is allowed, blocked, gated, or transitionable must not import a fuel module, and only the surfacing layer (the reason tool and the status text) may. A decision that cannot see fuel cannot key on it — so the only floors stay on named terminal defects (a CONFLICT verdict, an un-CONSISTENT gate), never on the fuel level. Importing a fuel module into any decision path breaks the test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…he DOX chains Update the src/vrf and src/runtime AGENTS.md contract blocks: the living world store (verdict-only, never parses the report), the fuel modules (pure math, context loader, tone-ladder directive, planner_reason tool), and two Stable Contracts — model-authored programs read only the verdict code, and reasoning fuel is tone-only (enters no allow/block decision). Plain, factual entries in the existing Domain Details style. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…on tail A live taskdep run showed 0 planner_elenchus_check / 0 planner_reason calls in ~1h across all reasoning steps: the fuel directive only rendered in planner_status (called 4x) and the reason-tool tails (never called -> chicken and egg), while the model reads planner_finish_step (78x) each step, where fuel never appeared. Surface it where the model actually looks: status.ts formatTransitionReasoningFuelTail renders the fuel line for the step a just-applied transition lands on, and the tool dispatcher (index.ts) appends it to the workflow-tool result. workflow-tools.ts only passes planPaths through as data; the dispatcher imports the formatter from the surfacing layer (status.ts), never a fuel module, so fuel-never-blocks holds. Presentational only, self-silences when no web/friction is warranted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
git inspection swells when generated bulk lands in the worktree untracked: a fresh project with a missing/empty .gitignore, or an existing project adopting a new tool whose artifact the current .gitignore misses. Add a generalized planning note to account for these and add a .gitignore task when the ignore rules are absent — without enumerating per-ecosystem names the model already knows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… streams Two bugs in the compaction banner: - The animated percent/bar advanced off elapsed time, and the SDK's setWidget has no diffing (every push disposes and rebuilds the component), so each update tore the aboveEditor banner down and reflowed the editor — the banner vanished and reappeared on every percent, jumping the text. Make the rendered line fully static for the run: reason + size, and the predicted duration as a fixed ~ETA hint, never a moving bar/percent. The widget setter's content dedup now pushes it once and skips every later tick; the interval only enforces the safety deadline. Drops the now-purposeless progress-bar helpers. - The banner did not clear while the model streamed its reply after a swallowed compaction: a streaming assistant reply arrives as message_update tokens (the signal the dashboard tracks), and message_start alone did not fire reliably for it. Clear the stale indicator on message_update too; the clear is idempotent so the per-token call is cheap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.