#pragma once #if !defined( RIPWIRE_MAIN_TU ) #error "verbs_for.h is a SECTION of src/main.cpp's translation unit - include it only from main.cpp (see the verb-family split note there)" #endif #include "infra/emit.h" // rw::emitTo / emitRaw / formatTo — THE emitter and its siblings #include "forliteral.h" // MF1-LIT: a task naming a literal gets its byte-exact sites and owner bodies, appended #include "forpage.h" // L-W: the --for --limit=N file page, coverage=, the thin rule and the widening next= #include "forowner.h" // the question's owner (owner-hop's found set) and the one owner body of an explanatory question #include "forfold.h" // body-fold: the whole bodies of the top ranked rows, appended to a compact-route answer #include "formembers.h" // member-names: mem= on the shown owner rows of a compact-route answer #include "forslots.h" // FOR-SLOTS: slots= on the shown initializer rows of a compact-route answer #include "forgraphverb.h" // FOR-ROUTE-DECLINED: the graph verb a `path from A to B` / `callers of X` task names, appended #include "forctorhop.h" // OWNER-CTOR-HOP: ctor_l=/ctor_calls= on the r=1 method row of a compact-route answer #include "fordispatchhop.h" // mf1-nx wave 4: a task-named call declined at an interface receiver carries its implementations #include "forcallerhop.h" // mf1-nx: a name-exact answer about ONE function carries that function's callers #include "epmanifest.h" // ep-manifest: the manifest entry-point groups that register a shown row #include "listedin.h" // manifest-noncode: the build/package/registry manifest lines that list a shown file (creation tasks) #include "forreexport.h" // ALIAS-IMPORT: the re-export hop rows of a definition the task names #include // std::ranges::find: the ep manifest re-picked from the rows emits #include // the redaction-tally snapshots a degraded pre-render restores #include // %.*s (precision, pointer) collapses to one view // FOR-ROUTE-DECLINED: the compact-legend layer's key for a captured verb answer (main.cpp, defined with that layer) static std::string_view compactLegendHint( const rw::Config& c, std::string_view doc ); // verbs_for.h — the QUERY family (§A2's contiguous dispatch block), moved VERBATIM from main.cpp in // the 2026-08-29 split: computeLensRanking (THE shared lens ranking — the change family's // --plan-lanes calls it too, which is why this section is included before verbs_change.h), // emitCandidates, the --for header/JSON/auto-bodies/compact-hops machinery, runForLens, // runTargetedViews (--lego/--exemplar/--recall/--query hoist) and runPackTask. Same contract as every // verbs_*.h: reopens main.cpp's unnamed namespace — one TU, one unnamed namespace, internal linkage // unchanged, zero new API surface — under the RIPWIRE_MAIN_TU guard. namespace { // The routed → anchored → mention-anchored → (opt-in) co-change-boosted lens rank for a task, plus the header // note fragments each stage contributes. This is THE ranking the --for lens consumes; extracted so runForLens // and runPackTask (L4) share ONE ranking implementation (the plan's "do not reimplement ranking" mandate). // rw::LensRanking itself now lives in packtask.h (L4) — shared with the MCP explore/pack_task verb's own // routed-ranking path (mcpverbs.h), which populates the SAME struct via the same low-level ranking calls. // r4/r6 (2026-09-10 lift-disclosure round): apply the lift and build its disclosure note in one call — "" for // either "the lift did not run" or "it ran and moved nothing" (the same silence-means-nothing-happened // convention mentionNote/boostNote/docMentionNote already follow). Pulled OUT of computeLensRanking (below, // already one of the largest functions in this file) rather than inlined at the call site like its three // siblings: those three predate this round and are not this change's to restructure, but two MORE // inlined branch-and-format blocks measurably worsened computeLensRanking's own complexity // (--quality-delta, 2026-09-10) for no reason the format text itself needs — the branch belongs beside the // mechanism it discloses, not beside the other three unrelated boosts it happens to run next to. inline std::string applySiblingLiftNoted( const rw::IngestResult& ing, std::vector& lensRank, std::size_t sibSeed, std::size_t sibPer ) { rw::SibliftLiftInfo liftInfo; if( !rw::applySiblingLift( ing, lensRank, sibSeed, sibPer, &liftInfo ) ) { return {}; } char nb[ 180 ]; rw::formatTo( nb, sizeof( nb ), " [sibling lift: promoted {} symbol{} in {} same-directory file{} of the top-ranked files (RIPWIRE_SIBLIFT={},{})]", liftInfo.symbolCount, liftInfo.symbolCount == 1 ? "" : "s", liftInfo.fileCount, liftInfo.fileCount == 1 ? "" : "s", sibSeed, sibPer ); return nb; } inline std::string applyStructuralExpansionNoted( const rw::IngestResult& ing, std::vector& lensRank, std::size_t expSeeds, std::size_t expPer ) { rw::ExpandLiftInfo liftInfo; if( !rw::applyStructuralExpansion( ing, lensRank, expSeeds, expPer, &liftInfo ) ) { return {}; } char nb[ 200 ]; rw::formatTo( nb, sizeof( nb ), " [structural expansion: promoted {} symbol{} in {} file{} reached via resolved import/reference edges from the top-ranked files (RIPWIRE_EXPAND={},{})]", liftInfo.symbolCount, liftInfo.symbolCount == 1 ? "" : "s", liftInfo.fileCount, liftInfo.fileCount == 1 ? "" : "s", expSeeds, expPer ); return nb; } // Compute the lens rank for `task` exactly as the --for path does (all existing boosts: routing, --anchor, // the B8 mention anchor, the B3 opt-in co-change prior). Pure function of (d, task): reads d.cfg for the same // flags --for reads, so both callers get identical rankings for the same query + flags. // `compactCandidate` — the caller is --for in a posture that may serve the COMPACT bundle, whose // section ORDERS a cut callee listing by this very rank vector. That makes it a FULL-DISTRIBUTION // consumer, like --adaptive and --anchor: a callee is not necessarily in the bundle's own top-K, so a // MaxScore-pruned score for it is not merely approximate, it is whatever the pruning left behind — and // the order of two callees would then depend on a performance optimization. postingscheck arm (e) found // exactly that (the same query returned two different callee orders with and without RIPWIRE_NO_PRUNE), // which is the gate doing its job: pruning is contracted to be BYTE-NEUTRAL, not nearly so. Measured // cost of giving it up on this route: none detectable — 384.7 vs 378.2 ms on django, 283.2 vs 269.8 ms // on webpack, 159.3 vs 159.0 ms on this repo, exhaustive at or inside the noise of pruned every time. rw::LensRanking computeLensRanking( const MainDispatch& d, std::string_view task, bool compactCandidate = false, bool fullDistribution = false ) // deep-tail: the file-grain tail reads the WHOLE // positive-score distribution (its total= is a // real count, and its order runs far past the // head), so the --for bundle path forces // exhaustive scoring — the --adaptive/--anchor // rule, for the same reason. Callers with no // tail (pack-task, candidates) keep the H2 // MaxScore pruning. { using namespace rw; const Config& cfg = d.cfg; const IngestResult& ing = d.ing; const Graph& g = d.g; const std::string& root = d.root; const bool multiRoot = d.multiRoot; const std::vector& ws = d.ws; // H2 (B0 r2): --for's consumers only read the top-K of this rank, so lexicalScores may skip symbols that // provably cannot enter that top-K (exact MaxScore pruning — emitted bytes identical). --adaptive/--anchor // are full-distribution consumers and force exhaustive scoring. // The route decides the ranker AND (with compactCandidate) whether pruning may run at all, so it is // chosen ONCE here and reused below rather than classified twice — chooseForRanker is pure over // (ing, task), so hoisting it is byte-neutral by construction. const bool routeOn = !cfg.noRoute; const RouteChoice rc = routeOn ? chooseForRanker( ing, task ) : RouteChoice{}; const bool compactRoute = routeOn && compactCandidate && rc.which == LexMode::SubtokenBody; std::size_t forPruneK = 0; std::vector ifaceExact; if( !cfg.adaptive && !cfg.anchor && !compactRoute && !fullDistribution ) { forPruneK = cfg.candidates ? ( cfg.topK > 0 ? std::size_t( cfg.topK ) : 0 ) : std::size_t( cfg.packTopN > 0 ? cfg.packTopN : 40 ); if( forPruneK > 0 && !cfg.candidates ) // candidates bypasses the lens bundle → no lego set { ifaceExact.assign( ing.symbols.size(), 0 ); for( std::size_t i = 0; i < g.implementors.size() && i < ifaceExact.size(); ++i ) { if( !g.implementors[i].empty() ) { ifaceExact[i] = 1; } } } } const std::vector* ifaceExactPtr = ifaceExact.empty() ? nullptr : &ifaceExact; // Query SHAPE (queryshape.h): a pasted trace / sanitizer report / compiler diagnostic, or a pasted // issue-template form. ROUTED PATH ONLY — route= is the one place this can be disclosed, so under // --no-route the classifier is not even asked and the ranking is byte-identical to what it always was. const queryshape::Verdict shape = routeOn ? queryshape::classify( task ) : queryshape::Verdict{}; // §P4 tier de-prioritization (filter.h): fixtures / present/ decks / generated captures score down, // folded INTO BM25 scoring (pruning-bound-safe) and BEFORE the B8 mention anchor — a fixture the task // literally NAMES is still lifted near the top. Plus, when a shape fired, the document tier. const std::vector tierMul = rankTierSymbolMultipliersShaped( ing, shape.fires() ); // Change logs and translations go LAST in the doc-mention lift below (filter.h docNoiseSymbolMultipliers), unless // the task asks about what those files hold. BM25 is untouched. Routed path only, like the shape. const std::vector docNoiseMul = routeOn ? docNoiseSymbolMultipliers( ing, task ) : std::vector{}; LensRanking out; std::vector& lensRank = out.rank; if( routeOn ) { lensRank = ( rc.which == LexMode::NameExact ) ? lexicalScoresNameExactRanked( ing, task, &tierMul ) : lexicalScoresTiered( ing, g.outOff, g.outTargets, task, forPruneK, ifaceExactPtr, &tierMul, 0, 0, {}, &out.evidence ); // L-W: the term evidence rides out if( rc.which == LexMode::NameExact ) { // L-W: the name-exact ranker scores WHOLE names, so the subtoken evidence coverage= and the file page // read comes from one exhaustive subtoken pass whose scores are discarded — pure lookups over the // persisted statistics (lexindex.h), a small fraction of a --for call (measured ~520 ms warm on a // 2,000-file C++ tree, most of it the cache load), paid on the identifier route only. lexicalScoresTiered( ing, g.outOff, g.outTargets, task, /*pruneTopK=*/0, nullptr, &tierMul, 0, 0, {}, &out.evidence ); } // §L10b + verify-wave2 F6: no leading space+"[" AND no trailing "]". This string lands ONLY in the // route= attribute value (and its JSON "route" twin) now, never spliced into free comment prose, so // the brackets that used to demarcate it inside a sentence just left the value opening with a stray // " [" and closing on an unbalanced "]". L10b took the leading half only, so every route value on // every dialect shipped one dangling bracket. A bracket is a PAIR or it is neither; the ATTRIBUTE // QUOTE is the delimiter here. The two stop-anchor helpers that grepped `routed: [^]]*` // (test/adaptivecheck.sh routeof(), test/routecheck.sh reasonOf()) are re-pinned to `[^"]*` in the // same commit — the quote is the real end of the value and was all along. out.routeNote = routeNoteOf( rc, shape, /*noRoute=*/false ); // row 6: the code itself, no "routed: " prose prefix — ONE producer (filter.h) out.docTierTag = shapeDocTierTag( shape ); // §A4f: the machine form of the same fact, for --format=candidates out.routeTag = ( rc.which == LexMode::NameExact ) ? "name-exact" : "subtoken+body"; // §A4f: the machine form of the same fact out.anchorDefs = std::move( const_cast( rc ).anchorDefs ); // empty unless the route was DECIDED by names (lexical.h) } else { lensRank = lexicalScoresTiered( ing, g.outOff, g.outTargets, task, forPruneK, ifaceExactPtr, &tierMul, 0, 0, {}, &out.evidence ); } // input blow-up guard disclosure (lexical.h kMaxUniqueQueryTerms/dedupeQueryTerms): a task string with // more distinct terms than the cap allows still scores — on the terms it kept — rather than refusing, so // the cut rides the SAME CapDisclosure channel every other indexing cap on this bundle uses (mention.h), // never a silent truncation. { CapDisclosure termsCap; termsCap.note( "terms_capped", "terms_total", out.evidence.termsCapped, out.evidence.termsSeenTotal ); absorbCapDisclosure( termsCap, out.capNote, out.capAttrs, out.capJson ); } // R4: capture the RAW routed lexical score's max BEFORE --anchor/mention/cochange reshape lensRank — the // honesty signal reads the actual textual evidence, not a graph-expanded or query-mention-boosted number // (those can promote a symbol the query's words never touched, which would mask a genuinely weak query). // The §P4 tier factor is divided back out for the same reason (maxScoreUndoingTier, filter.h). if( !lensRank.empty() ) { out.maxLexicalScore = maxScoreUndoingTier( lensRank, tierMul ); } // --anchor (EXPERIMENTAL): lexically-anchored graph expansion (byte-identical without the flag). if( cfg.anchor ) { lensRank = anchoredLexicalRank( g, lensRank ); } // B8 query-mention anchoring: lift a file / dotted module / Scope.symbol the task literally NAMES near the // top (inert — byte-identical — when the text names nothing indexed). Routed path only. if( !cfg.noRoute && !cfg.noMentionBoost && !std::getenv( "RIPWIRE_NO_MENTION" ) ) { MentionBoostInfo mentionInfo; if( applyMentionBoost( ing, task, lensRank, &mentionInfo ) ) { char nb[ 220 ]; rw::formatTo( nb, sizeof( nb ), " [mention anchor: {} file{} + {} symbols named in the task, score lifted to within 5% of the top score; " "mention_anchored= on the root repeats this total]", mentionInfo.fileCount, mentionInfo.fileCount == 1 ? "" : "s", mentionInfo.symbolCount ); out.mentionNote = nb; out.anchorLifts = mentionInfo.fileCount + mentionInfo.symbolCount; // §A4f: the count the candidates root emits } // Collected whether or not the anchor MOVED anything: a task whose named file fell outside the // extraction window lifts nothing, and is the run that most needs telling. absorbCapDisclosure( mentionInfo.caps, out.capNote, out.capAttrs, out.capJson ); } // r4 sibling lift (EXPERIMENTAL, pre-registered — bench/locbench/results/r4_siblift/PREREG.md): lift the // strongest query-relevant same-directory siblings of the top-ranked files into the slot ladder. INERT // (byte-identical) unless RIPWIRE_SIBLIFT="," parses in range. Routed path only. if( !cfg.noRoute ) { if( const auto [ sibSeed, sibPer ] = sibliftParams(); sibSeed > 0 ) { out.sibliftNote = applySiblingLiftNoted( ing, lensRank, sibSeed, sibPer ); } } // r5 file-level evidence pooling (EXPERIMENTAL, pre-registered — // bench/locbench/results/r5_pooling/PREREG.md): rank files by POOLED symbol evidence instead of // their single best symbol, so a file with five moderate hits can outrank one with a single sharp // one. Chooses no neighbour, which is the axis siblift failed on. INERT (byte-identical) unless // RIPWIRE_POOL="," parses in range. Routed path only. if( !cfg.noRoute ) { if( const auto [ poolK, poolBlend ] = filePoolParams(); poolK > 0 ) { applyFilePooling( ing, lensRank, poolK, poolBlend ); } } // r6 structural expansion (EXPERIMENTAL, pre-registered — // bench/locbench/results/r6_expansion/PREREG.md): lift the RESOLVED import/reference neighbours of // the top-ranked files. siblift had this seed with a same-directory edge; anchorhop had this edge // seeded from mention anchors; both were rejected. This is the untried diagonal, and the first // candidate that adds evidence the QUERY did not supply. INERT unless RIPWIRE_EXPAND=",". if( !cfg.noRoute ) { if( const auto [ expSeeds, expPer ] = expandParams(); expSeeds > 0 ) { out.expandNote = applyStructuralExpansionNoted( ing, lensRank, expSeeds, expPer ); } } // B3 co-change prior boost (OPT-IN, EXPERIMENTAL): files that historically change WITH the top seeds get a // bounded secondary boost. Inert (byte-identical) without usable git history. Routed path only. if( !cfg.noRoute && ( cfg.cochangeBoost || std::getenv( "RIPWIRE_COCHANGE" ) ) ) { PROFILE_SCOPE_DESCRIBE( "main: co-change prior boost (mine + apply)" ); std::vector> coSets; CommitWindowCensus coCensus; // the kCoBoostMaxFilesPerCommit census (gitmine.h) if( multiRoot ) { for( std::uint32_t r = 0; r < ws.size(); ++r ) { if( !hasEnclosingGitRepo( ws[r].arg ) ) { continue; } auto part = gitRecentCommitFileSets( ws[r].arg, ing, kCoBoostCommitWindow, kCoBoostMaxFilesPerCommit, r, &coCensus ); for( std::vector& c : part ) { coSets.push_back( std::move( c ) ); } } } else if( hasEnclosingGitRepo( root ) ) { coSets = gitRecentCommitFileSets( root, ing, kCoBoostCommitWindow, kCoBoostMaxFilesPerCommit, UINT32_MAX, &coCensus ); } CoBoostInfo boostInfo; // NOT guarded by coSets.empty(): applyCoChangeBoost records the commit-cap census BEFORE its own // empty check and then returns false, so calling it unconditionally is what makes the cap honest. // coSets is empty exactly when EVERY commit exceeded kCoBoostMaxFilesPerCommit -- the case where the // cap bit hardest -- and a `!coSets.empty() &&` short-circuit meant coboost_commits_capped was the // one disclosure never emitted at 100% drop. The return value still gates the boost NOTE alone. if( applyCoChangeBoost( ing, coSets, lensRank, &boostInfo, &coCensus ) ) { char nb[ 200 ]; rw::formatTo( nb, sizeof( nb ), " [cochange boost: promoted {} symbols in {} files that historically change with the top seeds (last {} commits)]", boostInfo.boostedSymbolCount, boostInfo.boostedFileCount, kCoBoostCommitWindow ); out.boostNote = nb; } absorbCapDisclosure( boostInfo.caps, out.capNote, out.capAttrs, out.capJson ); } // R5 — doc-mention surfacing (default-on, route-agnostic; see mention.h applyDocMentionBoost): // reuses g.mentions (the same doc<->code backtick edges `--mentions=SYM` already exposes) to lift docs // that discuss the query's top-resolved symbols, strictly below those symbols' own scores. Runs LAST // (after route/anchor/query-mention/co-change) so it reads the fully-resolved rank. if( !cfg.noDocMention && !std::getenv( "RIPWIRE_NO_DOC_MENTION" ) ) { DocMentionBoostInfo docMentionInfo; if( applyDocMentionBoost( g, lensRank, &docMentionInfo, docNoiseMul ) ) { char nb[ 220 ]; rw::formatTo( nb, sizeof( nb ), " [doc mentions: {} doc{} discussing {} top-ranked symbol{} surfaced; doc_mentions= on the root repeats the doc count]", docMentionInfo.docCount, docMentionInfo.docCount == 1 ? "" : "s", docMentionInfo.anchorCount, docMentionInfo.anchorCount == 1 ? "" : "s" ); out.docMentionNote = nb; out.docMentionCount = docMentionInfo.docCount; // §L10b: machine form for the doc_mentions= root attribute } absorbCapDisclosure( docMentionInfo.caps, out.capNote, out.capAttrs, out.capJson ); } return out; } // §P8/§P12.2 — --adaptive used to silently no-op under --format=candidates: both candidates dispatch sites // (the --for one below and --query's in runDefaultMap) returned before the cliff-cut logic ever ran, so the // flag was accepted and produced byte-identical output. It now cuts the SAME scored list candidates already // exports — one leading XML comment (candidates owns its own document root, so this mirrors how --query's // own default-map adaptive note is emitted: a comment ahead of the root, never inline inside it) plus the // clamped row cap. `ceiling` is the verb's own natural cap (--top-k, not the 40-row --for lens cap). // `scanFullDistribution` mirrors each CALLER's own default-map --adaptive behavior (true for --for, whose // 40-row cap can hide the true cliff; false for --query, whose ceiling already covers the full top-k). // One function (not inlined at each call site) so the two PRE-EXISTING dispatch functions this composes // into (runForLens/runDefaultMap) each gain a single call instead of the whole cut-and-print sequence. inline void emitCandidates( std::FILE* out, const rw::IngestResult& ing, const std::vector& rank, int topK, bool adaptive, bool scanFullDistribution, rw::CandidateProvenance prov, // §A4f: route/anchored/weak — the caller owns which ranker ran rw::RedactCounts* redact, // §B0/W3-N1: REQUIRED — is emitted text std::string_view candRootArg ) // R-R: the single-root root its p=/id= are relative to { int capN = topK; if( adaptive ) { const rw::AdaptiveCut ac = rw::adaptiveCut( rank, 5, std::size_t( topK ), scanFullDistribution ); // T14: same homonym-pool decline as --for's own bundle (lexical.h isAdaptiveHomonymDecline) — this // export shares the identical adaptiveCut call and must not narrow a large homonym pool on tie-break // order either. `prov.route` is the caller's own routing fact ("name-exact"/"subtoken+body"/"no- // route"/…) — read through isNameExactRouteTag, the one predicate every call site now shares // (review rv-t14a.md finding 1: runForLens's OWN inline check answered a different question and // mislabeled "no-route" as name-exact; this site was already correct, now on the shared function). const bool declined = rw::isAdaptiveHomonymDecline( rw::isNameExactRouteTag( prov.route ), ac, 5 ); char nb[ 208 ]; if( declined ) { rw::formatTo( nb, sizeof( nb ), "", ac.positiveHits, topK ); } else if( !ac.hitCeiling && ac.cliffRank < ac.kept ) { rw::formatTo( nb, sizeof( nb ), "", ac.kept, topK, ac.cliffRank, ac.dropPct, ac.kept ); } else if( !ac.hitCeiling ) { rw::formatTo( nb, sizeof( nb ), "", ac.kept, topK, ac.cliffRank, ac.dropPct ); } else if( ac.positiveHits <= ac.kept ) { rw::formatTo( nb, sizeof( nb ), "", ac.kept, topK, ac.positiveHits ); } else { rw::formatTo( nb, sizeof( nb ), "", ac.kept, topK ); } std::fputs( nb, out ); capN = declined ? topK : int( ac.kept ); } packCandidates( out, ing, rank, capN, redact, prov, candRootArg ); // R-R: root-relative p= + id= } // ── §A4a — the --for --json bundle, budgeted ──────────────────────────────────────────────────────────── // The JSON lens used to run NO size control at all: byte-identical at --token-budget=1000 and 20000 while // the XML sibling shrank 4.7x, so the JSON/MCP audience — the one that most needs a budget — had none. It // runs the same H1 ladder against the same budget the XML path computes, and SAYS what it did: "capped" is // the ladder's own verdict (never inferred by the caller) and "est_tokens" is the delivered size, mirroring // the XML `` / header `est_tokens="N"` attributes. // // The sigs array is rendered into memory first for exactly the reason the XML path buffers it: the TRUE // delivered byte count must be known before the header that reports it is written. Its own function (not a // branch inside runForLens) because it is a whole second serialization of the bundle — inlined, it made the // XML path harder to read for a reader who only cares about XML. struct ForLensJsonInputs { const rw::IngestResult& ing; const std::vector& rank; int topN; const std::vector* fanIn; const std::vector* impure; const std::vector* churnPerFile; const std::vector* cloneMember; const std::vector* tested; const std::vector* amp; rw::RedactCounts* redact; // §B0: the run's redaction tally — nullptr under --no-redact std::size_t packBudgetBytes; // per-entry streaming budget (--pack-budget-bytes) std::size_t tokenBudget; // --token-budget=N, 0 ⇒ the kForPayloadBudgetBytes default const rw::notes::NoteIndex* noteIndex; // §B1.3: L3 field notes, as the XML bundle already // receives them. nullptr ⇒ no notes keys at all. // §B1.4 (capture-audit-4): existence counts for the three sections that stay XML-only under --json — // the notes_total convention verbatim: "what the tree matched" BEFORE any display-side cap/dedup, not a // promise of exactly how many rows the XML sibling would print. legoTotal in particular can exceed the // XML's own row count (packLego dedups same-named interfaces and caps at topN=12) — that is by design, // not drift: the point is telling 0 (genuinely nothing on this surface) from N>0 (dropped, ask for XML), // not mirroring packLego's display-only collapsing. Always present (never conditional), because a key // that is sometimes ABSENT reintroduces the exact ambiguity this fix exists to remove. std::size_t legoTotal; std::size_t composeTotal; std::size_t routesTotal; // R-R (2026-08-24): the run's root, so this bundle's "p"/"id" are root-relative like the XML twin's. // The R-E harvest reached the XML --for bundle but left this JSON sibling's packSignaturesJson call // spelling `/*rootArg=*/{}` — the two dialects of ONE question disagreed about the spelling of a path. std::string_view rootArg; // DEEP-TAIL d2: the file-grain tail (FOLD C: page 0 of the --for file page, computed once above the dialect split, so // both dialects serve the same rows). REQUIRED, not defaulted — a defaulted pointer is how a dialect // silently loses a surface its XML twin serves. const rw::FileTail* fileTail; const rw::LexTermEvidence* evidence; // FOLD C: the term evidence the tail's page rows rank by (the lens run's own) // L3 follow-up (CodeRabbit 4053600616): read BEFORE the caller nulls noteIndex for emptiness (d.notesDegraded, // MainDispatch) — REQUIRED like fileTail above, for the same reason: a defaulted bool is how a degrade // silently drops off this dialect while the XML twin keeps carrying it. bool notesDegraded; // code above docs (serialize.h reorderDocsAfterCode): the XML twin's verdict, REQUIRED for the same reason bool docsAfterCode; // head round-robin (serialize.h reorderHeadRoundRobin): the XML twin's verdict, REQUIRED for the same reason bool headRoundRobin; // knob-honesty-068: what the "sigs_next" continuation echoes (serialize.h SigsCutContinuation) — REQUIRED like the two // above: a capped JSON bundle names the call that serves its cut rows exactly as the XML twin's does. std::string_view task; int packTopN; std::string_view rankFlags; // forRankShapingFlags( cfg ), owned by the caller for the call's duration }; // The lens bundle's opening keys. Every note is absent-unless-present — the same silence-means-nothing- // happened convention the XML header comment uses, so a reader never has to tell an empty string from a // missing stage. §A4e: `weak` is the one that used to be XML-only (string-spliced into a comment, hence // structurally unreachable from JSON); it is a real key here. struct ForLensNotes { const std::string& route; const std::string& mention; const std::string& boost; const std::string& siblift; // r4 EXPERIMENT — "" unless RIPWIRE_SIBLIFT actually promoted a symbol const std::string& expand; // r6 EXPERIMENT — "" unless RIPWIRE_EXPAND actually promoted a symbol const std::string& docMention; // §L10b: the XML twins of mention_anchored=/doc_mentions= (verbs_for.h ForLensHeaderParts) — same // absent-unless-present convention as `mention`/`docMention` above (0 only when the note above is // also ""; the emitter below gates on the STRING, not these, so a genuine zero can never occur here). std::uint32_t mentionAnchored = 0; std::uint32_t docMentions = 0; const std::string& adaptive; const std::string& floor; // LB-A: the relevance floor's shrink note, "" when it did not fire // the confidence disclosure is the exception to absent-unless-present: it is a FACT of every ranking // (the §A4e `weak` precedent — a root fact must be legible from either dialect, so the XML attrs get // real keys here rather than a string spliced somewhere JSON cannot reach). const char* confidence; // "high" | "low" int marginPct; // the whole-percent relative drop the confidence derives from (0 = none) int coveragePct; // L-W: the XML root's coverage= (-1 = absent: nothing scored) bool weak; // M10: gitstamp::stampAt(root) — the raw sha[+dirty] value, "" on multi-root/non-git (never "null" as // a STRING; the JSON emitter below distinguishes empty-string-absent from a real value the same way // every other JSON dialect's at= sibling does — see writeTestGateReportJson/situationDiffJson). const std::string& at; // ── HARNESS-FACING instrumentation, JSON dialect ONLY (docs/EVALS.md, "Agent Retrieval Bench — // abstention round 2: the adaptive cut's corpus-support facts", PRE-REGISTERED 2026-08-30) ─────── // The adaptive cut computes these three and, before this, emitted none of them on any surface. They // are the raw counts BEHIND confidence=/margin_pct=, not a second opinion about them: same // adaptiveCut call, same score vector, no new scorer and no second pass. Round one closed threshold // tuning over confidence=/margin_pct= as a recorded NEGATIVE and named exactly these as the next // candidate, so they exist here to be MEASURED against the benchmark's selective splits — not yet // to be read by an agent. Hence this dialect and nowhere else: the XML bundle's header rides a // measured byte ceiling (fornotesbudgetcheck.sh fits at est_tokens=800 exactly), and a fact that may // turn out to carry no signal has not earned bytes off every ranked answer. MCP `for` serves the XML // (forTaskText), so no agent surface moves. Promotion to XML root attributes — with the legend // clause, --help and skills text the surface-audit checklist demands — is what a POSITIVE // calibration outcome licenses, and is deliberately NOT done here. std::size_t keptCount; // AdaptiveCut::kept — the cliff-clamped head size, in [floor, ceiling] std::size_t scored; // AdaptiveCut::positiveHits — indexed symbols with a routed score > 0 std::size_t corpus; // symbols the lens scored at all — the denominator `scored` means nothing without // The XML root's cap attributes, already spelled as JSON key pairs by mention.h's CapDisclosure so the // two dialects cannot drift into two vocabularies. "" on every run where no indexing cap fired. std::string_view capJson; }; // W3FIX H2 — the pieces --for's header comment is made of, so the header can be REBUILT in three shapes (as // built / task echo dropped / that plus route=) for serialize.h's climbCeilingLadder to price. A free function // over a parts struct rather than a lambda inside runForLens: the ladder calls it up to three times, and // runForLens is already one of the largest functions in this file. Mirrors packtask.h PackTaskHeaderParts. struct ForLensHeaderParts { std::string_view task; // §B1.7's subject — the VERBATIM query, for the root attribute std::string_view rootOpenStr; // ctxRootOpen( task, routeNoteRaw ), pre-built (its size is charged) std::string_view taskNote; // the comment's scrubbed echo of `task` (xmlCommentText) std::string_view adaptiveNote, mentionNote, boostNote, docMentionNote; std::string_view sibliftNote, expandNote; // r4/r6 EXPERIMENTS — present only when the env-gated lift actually promoted something std::string_view floorNote; // LB-A: present only when the relevance floor actually shrank the quota // Ranking-confidence disclosure (paper-shape lane; arXiv 2607.24882 names abstention/confidence as the // unsolved retrieval axis). ALWAYS present — facts derived from the SAME adaptiveCut gap statistic // --adaptive cuts at, no new scorer, no behavior change. The attrs live HERE rather than in the // pre-built rootOpenStr because the ceiling ladder's route-dropped rung rebuilds the root open from // scratch (ctxRootOpen below) and would silently shed a fact spliced into the pre-built string. std::string_view confidenceAttrs; // e.g. ` confidence="high" margin_pct="34"` — root facts, every ladder rung std::string_view confidenceNote; // the legend sentence defining the two attributes (legend-coverage contract) std::string_view gitAtAttr; // M10: ` at="[+dirty]"` (gitstamp::atAttr) — "" on multi-root/non-git; // same ALWAYS-present, never-budget-dropped treatment as confidenceAttrs // above, for the same reason (a root fact, not a content row). std::string_view mentionDocAttrs; // §L10b: ` mention_anchored="N"`/` doc_mentions="N"`, EACH present only // when its own note fired (unlike confidenceAttrs/gitAtAttr, these are // not facts of every ranking) — machine form of mentionNote/docMentionNote, // spliced the same way so the ladder's route-dropped rebuild keeps it. bool anchor = false; // --anchor's EXPERIMENTAL caveat paragraph bool autoBundle = false; // T3: auto mode is on (cfg.detail==0, no --signatures-only) — appends the bundle=auto legend bool compactBundle = false; // COMPACT conceptual serving: appends the bundle=compact legend INSTEAD of the auto one — never both, because only one of the two sections can be emitted bool compactLegend = false; // --legend=compact: versioned, shorter explanatory dialect bool tailLegend = true; // deep-tail: the r=/ legend clause. forbudget-soft: never dropped any more // (the ceiling ladder's rung zero went with the hard ceiling); kept as the // one switch the header writers read bool idRouteLegend = true; // row 6 (2026-09-12): the sc=/route= readings (graphlegend.h kForIdRouteLegend); // never dropped either, for the same reason std::string_view rootArg; // R-E (2026-08-17): the single-root run's own root= — the ladder's // route-dropped rebuild below calls ctxRootOpen a second time and // must carry the SAME root as the pre-built rootOpenStr did. bool hdrLegend = false; // R2-AF (round 2, S4): present-only — set when forHdrRows (mention.h // rw::forNamedHeaderRows) is non-empty. NOT ceiling-droppable (unlike // tailLegend/idRouteLegend/confidenceNote): the rows themselves are a // small, bounded lookup outside the H1 payload budget, so their // definition rides with them rather than falling to the ceiling. bool scPresent = true; // PR #215 review: does any row this bundle serves carry sc=? The two // dialects' sc= readings are present-only on it, and the dropped-legend // note names sc= only when it had something to define. Defaults TRUE — // an over-approximation costs a reader nothing but a clause, while an // under-approximation is an undefined first-screen attribute. bool composePresent = false; // L1 fix: may a row ride this bundle? The compact dialect defines // present-only on it; the same over-approximation as scPresent (an edge // touching a ranked symbol), decided before the header is built. bool legoPresent = false; // TRAIN 9: may an row ride this bundle? Same // over-approximation and same timing as composePresent: a ranked symbol // with implementors, asked before the header is built rather than after // the section is rendered (the header is priced, and re-priced, from here). bool layerPresent = false; // TRAIN 9: does any ranked row's file sit under a built-in arch layer // directory, so its row carries layer=? Same over-approximation again. bool modScopePresent = false; // #60: does any ranked row name a file's MODULE SCOPE (t="modscope", // n=)? It reaches this bundle as a row and, more often, // as a hop row. Same over-approximation, same reason. bool endLinePresent = false; // may a row carry e= (serialize.h defEndLine)? A code row with a known // extent among the row head — read from the same head walk as // modScopePresent; a file the emitter then fails to read is the residual. bool viaPresent = false; // FE-B: may a row of this bundle carry via="name"? Some head row // (the same row head modScopePresent reads) has a name-only out-edge // (serialize.h namesOnlyOutAny). Over-approximates the 16-per-symbol cap. bool ownerPresent = false; // owner-hop: the compact carries a qword= row or a qword_cut= disclosure // (forOwnerHopPlan, decided before the header, cleared with the section) bool viaByOwnerOnly = false; // owner-hop: viaPresent holds ONLY because the owner rows joined the via question // (the six ranked hop ids alone answer no) — the via clause is then the owner // rows' disclosure, exempt from the sig trim exactly as the qword clause is bool bodySlotPresent = false; // for-body-slot: an explanatory question on the compact route named an owner // (forowner.h forBodySlotOwner), so a element rides — served, or skipped // under a target with its next= — and the slot clause defines it (present-only) bool splitPresent = false; // attrib-multidef Q1: may a row carry prov="split"? viaPresent's twin // question over the same ids (serialize.h splitArmsOutAny); set after construction bool hopsCallsNextPresent = false; // body-fold rider: a ranked row's cut carries next= (set after the // hops render, cleared with the section) bool bodyFoldPresent = false; // body-fold: a compact-route answer in the default regime has fold candidates // (forfold.h forBodyFoldCandidates), so a rides and its clause defines it; // set after construction, cleared with the section bool foldFollowPresent = false; // WRAPPER-FOLLOW (forfold.h renderFollows): the fold carries a (set after // the fold renders), so its clause defines it bool memberNamesPresent = false; // member-names (formembers.h): a shown owner row carries mem= (set after the // splice), so its clause defines it bool initSlotsPresent = false; // FOR-SLOTS (forslots.h): a shown initializer row carries slots= (set after the // splice), so its clause defines it bool slotJoinPresent = false; // IX1-V2 (forslots.h spliceSlotJoinRows): a shown row carries slot_by= / slot_fields= bool slotNamePresent = false; // fix1 F-4: a printed slot caller carries the field-name-only mark (`~`) bool ctorHopPresent = false; // OWNER-CTOR-HOP (forctorhop.h): the r=1 row carries ctor_l= (set after the splice) bool callerHopPresent = false; // mf1-nx: a name-exact answer whose body candidates are ONE function in the default // regime (forcallerhop.h forCallerHopTargets), so a rides and its clause // defines it; set after construction, cleared with the section bool callerHopRanked = false; // UPWARD-2HOP: that is the ranked seed's (by="rank"), so ITS clause rides instead bool dispatchHopPresent = false; // mf1-nx wave 4 (fordispatchhop.h): a rides; its clause defines it bool epPresent = false; // ep-manifest (epmanifest.h): a manifest entry-point group registers a shown row in // the default regime, so an rides and its clause defines it; cleared with it bool listedPresent = false; // manifest-noncode (listedin.h): a manifest lists a shown file on a creation task, so a // rides and its clause defines it; cleared with it }; // Splice a pre-formatted fragment in front of a structural boundary, or leave the document exactly as it // was. Every late splice in this file is this operation: three root attributes go in front of the FIRST // ">", and rootOpenWithExtraAttrs // below goes in front of a root open tag's '>'. All of them share the same fallback — an unexpected shape // leaves the document untouched rather than inserting at a guessed offset — and that fallback is the whole // of the error handling, which is why it belongs in one place instead of seven. // EMPTY IS A NO-OP, so a caller whose fragment did not fire needs no `if` of its own. inline void spliceBefore( std::string& doc, std::string_view boundary, bool fromEnd, std::string_view part ) { if( part.empty() ) { return; } const std::size_t at = fromEnd ? doc.rfind( boundary ) : doc.find( boundary ); if( at != std::string::npos ) { doc.insert( at, part ); } } // insert pre-formatted attributes (leading space, already attribute-safe — every caller's values come // from a fixed enum + an int or a versioned schema id, never corpus text) before the root open tag's '>'. inline std::string rootOpenWithExtraAttrs( std::string rootOpen, std::string_view attrs ) { spliceBefore( rootOpen, ">", /*fromEnd=*/false, attrs ); return rootOpen; } inline std::string rootOpenWithSchema( std::string rootOpen, std::string_view schema ) { if( schema.empty() ) { return rootOpen; } return rootOpenWithExtraAttrs( std::move( rootOpen ), " schema=\"" + std::string( schema ) + "\"" ); } // ── Ranking-confidence disclosure for --for (paper-shape lane; arXiv 2607.24882 names abstention/ // confidence as the unsolved retrieval axis) ───────────────────────────────────────────────────────── // The MAPPING, from facts adaptiveCut already computed — pure, deterministic, and stated in the legend // sentence it also builds. high on either of two grounds: a MATERIAL cliff inside the served window // (!hitCeiling — the sharp-query shape, margin_pct= is that drop), or the served head already contains // EVERY positive match (nothing beyond it for a cut to get wrong; the caller passes the FINAL head size, // after the relevance floor trimmed the zero-score tail). Everything else is low: a flat head with a // longer positive tail — the shape where a fixed head is a guess — including the zero-hit query // (positiveHits==0 fails the completeness ground's >0 guard deliberately: a ranking of nothing is not a // trustworthy ranking). margin_pct reports the IN-WINDOW drop only (0 under hitCeiling): a material drop // far beyond the served head cannot justify trust in the head, and quoting it here would claim exactly // that. A free function over the cut rather than more lines in runForLens, which is already one of the // largest functions in this file (the forLensHeaderText precedent). // A6/H14: ForConfidence + deriveForConfidence moved to lexical.h, beside the adaptiveCut they read — the // MCP `for` twin serves confidence=/margin_pct= from the SAME derivation now instead of omitting the // routing trust gauge entirely (capture-audit H14). Nothing about the numbers changed. using rw::ForConfidence; using rw::deriveForConfidence; // A1′ (owner decision 2026-09-12): --for's COMPACT legend, pinned like every other verb's (test/compactlegendcheck.sh, the // ripwire.for/v1 row) and PRESENT-ONLY — every clause below is emitted only when the document carries the // attribute it defines, and every reading is the shortest honest one. It used to spend 1,177–1,216 B here // (the same sentences as the default dialect with a schema id in front) and was exempt from the per-verb // pin by name; a compact dialect exempt from being compact is not one. The data notes (relevance floor, // doc mentions, mention anchor, co-change boost) keep their NUMBERS and lose their explanation // (compactForNote below); adaptive/siblift/expansion notes are kept verbatim — their text IS the data // (cliff rank, drop, the env knob). No "--" anywhere: an XML comment (G4). // // WHAT RIDES WHERE. Root facts that the root's own attributes state (bundle=/bodies=/reason=) get one // clause; the lens columns one clause (cx/ccx/in/churn/amp/clone/tested are always computed on this // surface, clone=/tested= appear only where 1); sc= and next= ride the clause; route= only when the // root carries it (the ladder's rung (c) drops the attribute, and then its reading goes with it); the // confidence, sc=/route= and tail clauses always ride (forbudget-soft: no ceiling ladder sheds them); the compact bundle's hops/calls/noedge clause and the auto bundle's bodies clause are // mutually exclusive by construction (planForEnrichment). inline constexpr std::string_view kForCompactLegendRoot = "bundle=/bodies=/reason= the body posture; "; inline constexpr std::string_view kForCompactLegendRows = "d: cx= ccx= complexity, in= callers (absent cx/ccx/in = 0), churn= amp= change, clone= tested= 1, sc= scope, id=p::sc::n; " "total= shown= capped=1 if cut"; // …and the same clause for a bundle whose served rows carry NO scope (a corpus of free functions, a markdown // tree): 22 B of sc= reading removed, nothing else. Two constants rather than one built at runtime because this // dialect's whole contract is a pinned byte count per schema, and a pin reads a constant. inline constexpr std::string_view kForCompactLegendRowsNoScope = "d: cx= ccx= complexity, in= callers (absent cx/ccx/in = 0), churn= amp= change, clone= tested= 1; " "total= shown= capped=1 if cut"; static_assert( kForCompactLegendRows.find( rw::kForZeroAbsentCompactNote ) != std::string_view::npos && kForCompactLegendRowsNoScope.find( rw::kForZeroAbsentCompactNote ) != std::string_view::npos, "the compact zero reading is the exempt note, verbatim (graphlegend.h forZeroNoteBytes)" ); // L1 fix round (2026-09-19, rv-r1-L1 HIGH-1): task= (the root's echo of the query), next= (the follow-up on a row), pure= // (a row flag) and the rows rode every default answer with no reading in either dialect; // legendcoveragecheck's default rows now fail on that. Charged like the rows clause; runForLens caps the compact charge at // the full dialect's, so defining them never costs the default a row (compactlegendcheck (P1)). inline constexpr std::string_view kForCompactLegendFacts = "; task= the query; d pure=1 const/constexpr sig, next= the follow-up to paste"; // The block's rows (serialize.h packCompose), present-only on composePresent. inline constexpr std::string_view kForCompactLegendCompose = "; field name= type= owner= rel=: a member of owner=, rel=creates held by value, uses by reference/pointer"; // TRAIN 9: two attributes the merged stack put on a DEFAULT --for first screen that neither member saw alone. // L2's --sections=lego,compose serves the lego section WHOLE, so reaches the rows; and a // query that ranks a file under a built-in arch layer dir (test/, bench/, infra/, render/ …) puts layer= on its // row. Both present-only, on the same terms as the compose clause above: absent, they cost nothing. inline constexpr std::string_view kForCompactLegendLego = "; iface implementors=N: types implementing it, m= its method contract"; inline constexpr std::string_view kForCompactLegendLayer = "; d layer=: built-in arch layer (game|infra|render|math|audio|ai|test) from a dir name in p="; // ONE spelling for both dialects (graphlegend.h kForRouteCodeLegend): the compact dialect and the default one // say the same thing about route=, so they cannot drift into two readings of one code. inline constexpr std::string_view kForCompactLegendRoute = rw::kForRouteCodeLegend; inline constexpr std::string_view kForCompactLegendConfidence = "; confidence=/margin_pct= head score drop (low=flat)"; inline constexpr std::string_view kForCompactLegendHops = "; h l= p= n=, c n= l= (joined for same-named callees, shown= counts them), noedge= no callee resolved"; // owner-hop: present-only, right after the hops clause, on the same plan the rows come from. Says it is BY NAME (a lookup in // the ranked head, never a resolution), widens noedge= to the owners, and defines the two disclosures an owner cut carries. // What qword= does NOT mean: that the row is the only, or the true, implementation of the question's subject. inline constexpr std::string_view kForCompactLegendOwner = "; h qword= its name (case-folded, leading _#$@ dropped) equals that question word (by name, not resolved, not the only one), first, calls to 100 (capped=1 next= all); " "qword_cut= owners not so served, next= the first"; // owner-hop: the full dialect's reading of the same two attributes (present-only, after the clause above; no "--": a comment) inline constexpr std::string_view kForOwnerHopLegend = "; hops row qword= the question word that row's NAME equals (ASCII case-folded, leading _ # $ @ dropped; by name in the " "ranked head, a lookup, not a resolution; it " "does not mean the symbol is the only or the true implementation): such an owner row comes first (a word on a short " "common-verb list such as get, set, run or write ranks after the other owner words) and its calls child " "lists up to 100 callee names (capped=1 with next= naming the full list when the guard or the budget cut it); an owner " "with no resolved callee is counted in noedge= like any ranked symbol. hops qword_cut=N: owners not served as a qword " "row (the three-row cap or the budget; one of the six ranked candidates still keeps its plain row), next= the first"; // …and the one question both dialects ask: the clause in this dialect, or nothing (ownerPresent is set only on the compact // route and cleared with the section, so it never rides without the hops clause in front of it). inline std::string_view ownerHopLegend( const ForLensHeaderParts& p, bool compact ) noexcept { if( !p.ownerPresent ) { return {}; } return compact ? kForCompactLegendOwner : kForOwnerHopLegend; } // …and what the sig-trim ledger exempts for the owner rows in this dialect: the qword clause, plus the via="name" clause when // the owner rows alone switched it on (a base answer without them never carried it, so charging it cost a row: // fastify-13 Context r23, pdoc AstInfo r38). ownerPresent and viaPresent are cleared with their sections, so neither part // is exempted for a clause the header did not write. inline std::size_t ownerLegendExemptBytes( const ForLensHeaderParts& p, bool compact ) { const bool viaByOwner = p.viaPresent && p.viaByOwnerOnly; const std::size_t viaBytes = !viaByOwner ? 0u : ( compact ? rw::kForCompactViaNameClause.size() : rw::forViaNameClause().size() ); return ownerHopLegend( p, compact ).size() + viaBytes; } inline constexpr std::string_view kForCompactLegendBodies = "; b t= n= p= l= full bodies, c n= l= callee signatures"; // for-body-slot: the compact dialect's reading of the ONE owner body (the full dialect's is forowner.h kForBodySlotLegend + the // root clause below), present-only (bodySlotPresent), right after the owner clause. No "--": it rides inside an XML comment. // FIX1 R3: it states what the full one states — what each pick= means and what reason=owner does NOT mean (the subject, the // whole answer): the default posture is this dialect, so this is the only reading most readers get. inline constexpr std::string_view kForCompactLegendBodySlot = "; b t= n= p= l= full bodies, c n= l= callee signatures; bodies=1 reason=owner: ONE body, the question's owner by the rule " "(pick=name: its name is a question word; pick=member: a method so named of a ranked class), chosen by name not resolved; " "not necessarily the subject, not a claim it is the whole answer; shown=0 capped=1 next= when a budget skipped it"; static_assert( kForCompactLegendBodySlot.starts_with( kForCompactLegendBodies ), "the slot's compact clause defines as the bodies clause does" ); inline constexpr std::string_view kForBodySlotRootClause = "; when it rides this root reads bodies=1 reason=owner"; inline std::string bodySlotLegend( const ForLensHeaderParts& p, bool compact ) { if( !p.bodySlotPresent ) { return {}; } return compact ? std::string( kForCompactLegendBodySlot ) : std::string( rw::kForBodySlotLegend ) + std::string( kForBodySlotRootClause ); } // body-fold rider (forbudget-soft N3): a ranked hop's cut listing carries next= (serialize.h packHops), defined present-only // (hopsCallsNextPresent: read off the rendered ), so an answer without a cut listing is byte-identical to before inline constexpr std::string_view kForCompactHopsCallsNextClause = "; h calls capped=1 next= the call listing every callee"; inline constexpr std::string_view kForHopsCallsNextClause = "; a hop row's cut calls listing (capped=1) carries next=, the call that lists every callee"; // does a RANKED hop row (an without qword=: an owner row's cut listing is the owner clause's) carry a cut with next= inline bool rankedHopCallsNextPresent( std::string_view hops ) noexcept { for( std::size_t at = hops.find( "', at ); const std::size_t rowEnd = hops.find( "", at ); if( tagEnd == std::string_view::npos || rowEnd == std::string_view::npos ) { return false; } const std::string_view tag = hops.substr( at, tagEnd - at ); const std::size_t open = hops.find( "', open ); if( tag.find( " qword=\"" ) == std::string_view::npos && open < rowEnd && shut != std::string_view::npos && hops.substr( open, shut - open ).find( " capped=\"1\" next=\"" ) != std::string_view::npos ) { return true; } } return false; } // A present-only clause in this dialect: "" unless its element or attribute rides, else the compact or the full spelling. inline constexpr std::string_view presentOnlyClause( bool present, bool compact, std::string_view compactClause, std::string_view fullClause ) noexcept { return !present ? std::string_view() : ( compact ? compactClause : fullClause ); } // mf1-nx / UPWARD-2HOP: the caller hop's present-only clause — the named-route one, or the ranked seed's (by="rank") inline constexpr std::string_view callerHopClause( const ForLensHeaderParts& p, bool compact ) noexcept { return p.callerHopRanked ? presentOnlyClause( p.callerHopPresent, compact, rw::kForCompactRankedCallerHopLegend, rw::kForRankedCallerHopLegend ) : presentOnlyClause( p.callerHopPresent, compact, rw::kForCompactCallerHopLegend, rw::kForCallerHopLegend ); } // mf1-nx: the name-exact route's hop clauses, present-only, in order — the caller hop's (named-route or ranked, above), then the dispatch hop's (wave 4) inline std::string hopClauses( const ForLensHeaderParts& p, bool compact ) { std::string out( callerHopClause( p, compact ) ); out += presentOnlyClause( p.dispatchHopPresent, compact, rw::kForCompactDispatchHopLegend, rw::kForDispatchHopLegend ); return out; } // FOR-SLOTS (slots=) and OWNER-CTOR-HOP (ctor_l=/ctor_calls=): the two -row splices' clauses, then ep-manifest's clause (epmanifest.h), each present-only, in that order inline std::string peerRowClauses( const ForLensHeaderParts& p, bool compact ) { std::string out( presentOnlyClause( p.initSlotsPresent, compact, rw::kForCompactInitSlotsLegend, rw::kForInitSlotsLegend ) ); out += presentOnlyClause( p.slotJoinPresent, compact, rw::kForCompactSlotJoinLegend, rw::kForSlotJoinLegend ); // IX1-V2 out += presentOnlyClause( p.slotNamePresent, compact, rw::kForCompactSlotNameLegend, rw::kForSlotNameLegend ); // fix1 F-4 out += presentOnlyClause( p.ctorHopPresent, compact, rw::kForCompactCtorHopLegend, rw::kForCtorHopLegend ); out += presentOnlyClause( p.epPresent, compact, rw::kForCompactEpManifestLegend, rw::kForEpManifestLegend ); // ep-manifest out += presentOnlyClause( p.listedPresent, compact, rw::kForCompactListedInLegend, rw::kForListedInLegend ); // manifest-noncode return out; } inline std::string_view hopsCallsNextLegend( const ForLensHeaderParts& p, bool compact ) noexcept { return presentOnlyClause( p.hopsCallsNextPresent, compact, kForCompactHopsCallsNextClause, kForHopsCallsNextClause ); } // body-fold: the fold's clause in this dialect, present-only (bodyFoldPresent), right after the slot clause // WRAPPER-FOLLOW: the follow_of= clause rides right after it, present-only (foldFollowPresent) inline std::string bodyFoldLegend( const ForLensHeaderParts& p, bool compact ) { return std::string( presentOnlyClause( p.bodyFoldPresent, compact, rw::kForCompactBodyFoldLegend, rw::kForBodyFoldLegend ) ) + std::string( presentOnlyClause( p.foldFollowPresent, compact, rw::kForCompactFoldFollowLegend, rw::kForFoldFollowLegend ) ); } inline constexpr std::string_view kForCompactLegendTail = "; t p= file outside sigs (weaker), t sym= its top symbols (a pointer, not the answer), r= rank (gap = trimmed)"; // R2-AF (round 2, S4): the COMPACT dialect's `` clause — verbatim, round-2 amendment §R7. // 96 B, present-only like every clause in this dialect (appendCompactForLegend below). inline constexpr std::string_view kForCompactLegendHdr = "; hdr p= of=: the named file's one same-dir same-stem decl/impl partner, listed first (a lookup)"; // The data notes in their compact spelling: the numbers stay, the sentence goes. Unknown shapes pass // through VERBATIM — a note this table does not know is never shortened into something it did not say. // ONE note (" [...]"); compactForNote below splits a channel that carries several. inline std::string compactOneForNote( std::string_view note ) { // " [relevance floor: kept 7 of 40 - the other 33 scored zero…]" → " [floor: kept 7 of 40]" if( note.starts_with( " [relevance floor: kept " ) ) { const std::size_t cut = note.find( " - " ); return cut == std::string_view::npos ? std::string( note ) : " [floor: kept " + std::string( note.substr( 24, cut - 24 ) ) + "]"; } // " [doc mentions: 2 docs discussing 1 top-ranked symbol surfaced; doc_mentions= …]" → " [doc mentions: 2 docs, 1 symbol; doc_mentions=]" if( note.starts_with( " [doc mentions: " ) ) { const std::size_t disc = note.find( " discussing " ); const std::size_t top = note.find( " top-ranked symbol" ); if( disc != std::string_view::npos && top != std::string_view::npos && top > disc ) { return " [doc mentions: " + std::string( note.substr( 16, disc - 16 ) ) + ", " + std::string( note.substr( disc + 12, top - disc - 12 ) ) + " symbol" + ( note.substr( top + 18 ).starts_with( "s" ) ? "s" : "" ) + "; doc_mentions=]"; } return std::string( note ); } // " [mention anchor: 1 file + 2 symbols named in the task, …; mention_anchored= …]" → " [mention anchor: 1 file + 2 symbols; mention_anchored=]" if( note.starts_with( " [mention anchor: " ) ) { const std::size_t cut = note.find( " named in the task" ); return cut == std::string_view::npos ? std::string( note ) : std::string( note.substr( 0, cut ) ) + "; mention_anchored=]"; } // " [cochange boost: promoted N symbols in M files that historically …]" → " [cochange boost: promoted N symbols in M files]" if( note.starts_with( " [cochange boost: promoted " ) ) { const std::size_t cut = note.find( " that " ); return cut == std::string_view::npos ? std::string( note ) : std::string( note.substr( 0, cut ) ) + "]"; } return std::string( note ); } // A note channel can carry SEVERAL notes back to back (the doc-mention channel also carries its cap note, // mention.h absorbCapDisclosure). Each " [...]" is compacted on its own, so a later note is never // swallowed by an earlier note's rewrite (which keeps only the first note's numbers). inline std::string compactForNote( std::string_view note ) { std::string out; while( !note.empty() ) { const std::size_t next = note.find( "] [" ); const std::size_t len = next == std::string_view::npos ? note.size() : next + 1; out += compactOneForNote( note.substr( 0, len ) ); note.remove_prefix( len ); } return out; } // Does THIS answer's root carry route=? The reading of a code is present-only in both dialects, so both ask the // question the same way: the ladder's rung (c) may have dropped the attribute (withRouteAttr), and a run the // router never ran for (no-route, or a verb with no route at all) never had one. Read off the built root open // rather than re-derived from the route reason, so the clause can only appear beside the attribute it defines. inline bool forRouteAttrPresent( const ForLensHeaderParts& p, bool withRouteAttr ) noexcept { return withRouteAttr && p.rootOpenStr.find( " route=\"" ) != std::string_view::npos; } // …and the THIN question, asked the same way: does this root carry L-W's coverage=? Four sites spelled the // same find() (two legend appends and two dropped-note selections), which is three chances to change it in two. inline bool forCoverageAttrPresent( const ForLensHeaderParts& p ) noexcept { return p.confidenceAttrs.find( " coverage=\"" ) != std::string_view::npos; } inline void appendCompactForLegend( std::string& h, const ForLensHeaderParts& p, bool withRouteAttr, std::string_view extraNotes ) { h += ""; // M10: at= folded into the SAME trailing comment as root= (one wrapper, not two) — the // weak=/dropped_positive=/est_tokens= splice below still targets the LAST comment's closing "-->" // (headerStr.rfind), unchanged from before this attribute existed. h += rw::forRootRelPathsLegendShort( !p.rootArg.empty(), !p.gitAtAttr.empty() ); } // T3 — the legend sentence for the terminal-by-default bundle, a named constant so the sigs-budget // exemption below (the D2 adaptiveNote precedent) subtracts EXACTLY the bytes the legend adds. No "--" // anywhere in it: it rides inside an XML comment, where "--" is ill-formed (G4). It also defines the // / disclosure trio, because those attributes now appear on --for's first screen and the // legend-coverage contract is that every first-screen attribute is defined in the emitting verb's legend. inline constexpr std::string_view kForAutoBundleLegend = "; bundle=auto: the top-ranked FULL bodies ride inline after the signatures in a bodies section " "(bodies=N on this root counts them; bodies=0 reason=budget when none fit the remaining budget " "whole; the signatures-only flag (no-bodies mode) opts out) — read them here instead of opening the files. The " "bodies element discloses the house way: total=requested, shown=printed, capped=1 when they " "differ; each body's calls child lists its callee signatures, total= always, shown=/capped= only " "when that list is cut"; // The clause defining the degraded reason, spliced into the header ONLY on that path (no "--": it rides in a comment). inline constexpr std::string_view kForDegradedBundleNote = "; reason=degraded: the signature block could not be measured, so no bodies were served and est_tokens is omitted"; // COMPACT conceptual serving — the legend that REPLACES the one above on the subtoken+body route // (pre-registered: docs/EVALS.md, the T3 route-narrowing round). Same constraints as its sibling: a // named constant so the sigs-budget exemption subtracts exactly what it adds, and no "--" anywhere, // because it rides inside an XML comment where "--" is ill-formed (G4) — which is why the two flags it // mentions are named without their dashes. // // IT IS DELIBERATELY TERSE, and the terseness is enforced rather than merely intended: the compact // surface has ONE byte allowance (kForCompactSurfaceBudgetBytes) that this legend is charged against, // so every byte spent explaining the section is a byte of edge context the section cannot serve. That // is the honest shape for a disclosure — it competes with the content it describes instead of riding // free — and the static_assert below is what stops a future edit from quietly eating the payload. inline constexpr std::string_view kForCompactBundleLegend = "; bundle=compact: conceptual query, so this map ships one-hop EDGE context, no bodies (bodies=0, " "reason=compact-route or no_candidates). hops rows are h l=line p=file n=name, and a row's calls " "child names its callees (c n= l=; l= comma-joins the lines of same-named callees, shown= counts callees). hops and calls disclose total=requested shown=printed capped=1 " "when the BUDGET cut a listing; noedge=N counts ranked symbols with no RESOLVED callee found (never " "none exists). For a body: expand=p:n pasted off a row; the auto-bodies flag puts the bodies back"; // One spelling of --for's header, three shapes of it. `withTaskEcho=false` replaces the comment's echo with a // note pointing at the task= attribute that still holds the verbatim copy — the duplicate goes, nothing else. // Byte-identical to the pre-ladder header when both flags are true and extraNotes is empty (golden-neutral). inline std::string forLensHeaderText( const ForLensHeaderParts& p, bool withRouteAttr, bool withTaskEcho, std::string_view extraNotes ) { std::string h; h.reserve( 640 + std::max( kForAutoBundleLegend.size(), kForCompactBundleLegend.size() ) + p.rootOpenStr.size() + p.taskNote.size() + p.adaptiveNote.size() + p.mentionNote.size() + p.boostNote.size() + p.sibliftNote.size() + p.expandNote.size() + p.docMentionNote.size() + p.floorNote.size() + p.confidenceAttrs.size() + p.confidenceNote.size() + p.gitAtAttr.size() + p.mentionDocAttrs.size() + extraNotes.size() ); h += rootOpenWithSchema( rootOpenWithExtraAttrs( rootOpenWithExtraAttrs( rootOpenWithExtraAttrs( withRouteAttr ? std::string( p.rootOpenStr ) : rw::ctxRootOpen( p.task, {}, p.rootArg ), p.confidenceAttrs ), p.gitAtAttr ), p.mentionDocAttrs ), p.compactLegend ? "ripwire.for/v1" : std::string_view() ); if( p.compactLegend ) { appendCompactForLegend( h, p, withRouteAttr, extraNotes ); return h; } h += ""; // W3-S item 5 (2026-08-19): the --for lens carries root= on its with nothing defining it — the // same "an attribute the document never explains" gap the root-relative round closed on eighteen other // legends via the shared rw::kRootRelPathsLegend clause (graphlegend.h), missed here because this // header is built in main.cpp rather than through a shared emitter. Pasting THAT clause verbatim was // tried and reverted: at 159 B it took a --token-budget=800 bundle's est_tokens from inside the ceiling // to 811 (+1.4%) and red test/fornotesbudgetcheck.sh — a real "a disclosure has BYTES" trap, on a // contract that says est_tokens <= the stated budget in both dialects. // // Trade-off chosen: a SHORTER spelling for this one call site, not a floor recalibration. Recalibrating // the shared budget constants (kMinBytesPerToken / kBudgetHeadroom / kCeilingFirstEntryTolerance, // serialize.h) would move every OTHER --for/--pack-task gate pinned against them // (bundleidcheck.sh/partitioncheck.sh/estchargecheck.sh among others) for one verb's 21-byte gap — a // blast radius wildly out of proportion to the fix. A second wording is normally exactly the kind of // echo-site drift kRootRelPathsLegend's own header warns against (it was hoisted OUT of eighteen // per-verb copies for that reason) — but --for is the one call site with a MEASURED, hard byte // constraint the other eighteen do not carry, and the two dialects (CLI --for, MCP `for`) share this // ONE short form, so there are still only two spellings in the whole tool, not nineteen. // Measured: 159 B (kRootRelPathsLegend) -> 126 B (kForRootRelPathsLegendShort), saving 33 B. On the // --token-budget=800 fixture fornotesbudgetcheck.sh builds: WITHOUT any root= clause, est_tokens=799 // (1 B of headroom under the 800 ceiling); WITH kRootRelPathsLegend appended, est_tokens=811 (RED, the // regression this comment records); WITH the short form below, est_tokens=800 (fits exactly — the // clause is now the FIRST byte of headroom this bundle had left, not eleven tokens past it). // `on` is p.rootArg's OWN presence, the same convention rootRelPathsLegend(bool) itself uses elsewhere // (never re-derived from withRouteAttr or anything else the ceiling ladder decided above). // M10: at= folded into the SAME trailing comment as root= (one wrapper, not two) — the // weak=/dropped_positive=/est_tokens= splice above still targets the LAST comment's closing "-->" // (headerStr.rfind), unchanged from before this attribute existed. h += rw::forRootRelPathsLegendShort( !p.rootArg.empty(), !p.gitAtAttr.empty() ); return h; } inline std::string forLensJsonHeader( std::string_view task, const ForLensNotes& notes ) { using rw::jsonStr; // M10: "at":null (never a fake sha) on multi-root/non-git — same convention as writeTestGateReportJson's // own at= sibling in this tool. A root fact, so it rides first, beside task=. std::string h = "{\"at\":" + ( notes.at.empty() ? std::string( "null" ) : ( "\"" + notes.at + "\"" ) ) + ",\"task\":\"" + jsonStr( task ) + "\""; if( !notes.route.empty() ) { h += ",\"route\":\"" + jsonStr( notes.route ) + "\""; } // the XML twin of these two fields carries task_scrubbed=/route_scrubbed= when the scrub lost bytes; this // dialect is the faithful one and says so from its own side (serialize.h ctxRootJsonScrubKeys). Absent on // clean input, so no ordinary document moves a byte. h += rw::ctxRootJsonScrubKeys( task, notes.route ); if( !notes.mention.empty() ) { h += ",\"mention\":\"" + jsonStr( notes.mention ) + "\""; } // §L10b: the XML twin's mention_anchored=. Gated on the COUNT since the cap-disclosure lane — the note // above can now carry a cap clause on a run that anchored nothing, and "mention_anchored":0 would be a // fabricated zero. if( notes.mentionAnchored > 0 ) { h += ",\"mention_anchored\":" + std::to_string( notes.mentionAnchored ); } if( !notes.boost.empty() ) { h += ",\"boost\":\"" + jsonStr( notes.boost ) + "\""; } if( !notes.siblift.empty() ) { h += ",\"siblift\":\"" + jsonStr( notes.siblift ) + "\""; } if( !notes.expand.empty() ) { h += ",\"expand\":\"" + jsonStr( notes.expand ) + "\""; } if( !notes.docMention.empty() ) { h += ",\"doc_mention\":\"" + jsonStr( notes.docMention ) + "\""; } // §L10b: the XML twin's doc_mentions=, gated on the COUNT for the same reason as mention_anchored above. if( notes.docMentions > 0 ) { h += ",\"doc_mentions\":" + std::to_string( notes.docMentions ); } // …and the cap keys, verbatim from CapDisclosure — the XML root's own facts, same names, same order. h.append( notes.capJson ); if( !notes.adaptive.empty() ) { h += ",\"adaptive\":\"" + jsonStr( notes.adaptive ) + "\""; } // LB-A: the XML twin appends this note to the header comment; the faithful dialect gets its own key. if( !notes.floor.empty() ) { h += ",\"relevance_floor\":\"" + jsonStr( notes.floor ) + "\""; } // ranking-confidence facts — always present (a fact of every ranking, mirroring the XML root attrs). h += ",\"confidence\":\""; h += notes.confidence; h += "\",\"margin_pct\":" + std::to_string( notes.marginPct ); if( notes.coveragePct >= 0 ) { h += ",\"coverage\":" + std::to_string( notes.coveragePct ); // L-W: the XML root's third ranking fact, same presence rule } // the adaptive cut's own counts — the abstention round-2 instrumentation described on ForLensNotes. // ALWAYS present (a count of zero is a measurement: "nothing scored", never "the field was dropped"), // and JSON-only BY DESIGN, which the struct's comment states in full rather than repeating here. h += ",\"kept\":" + std::to_string( notes.keptCount ); h += ",\"scored\":" + std::to_string( notes.scored ); h += ",\"corpus\":" + std::to_string( notes.corpus ); if( notes.weak ) { h += ",\"weak\":true"; } return h; } // §B1.3: the auto-surfaced field notes ride the rows inside the sigs array; this stanza is their DISCLOSURE // — `notes_total` counts what the tree matched, `notes_kept` what survived the byte ladder, the same pair // --pack-task --json reports. Empty unless a NoteIndex exists, so a tree with no .ripwire_notes keeps its // pre-feature bytes exactly (the L3 inertness contract). notes_degraded= is a SEPARATE, independent splice at // the call site (below) rather than a third parameter here — `active` gates notes_total/kept, a fully-degraded // read (every line unparsed) can leave `active` false with nothing to count, and the two facts must not be // entangled into one signature. inline std::string forLensNotesStanza( const rw::JsonSigNoteCounts& counts, bool active ) { if( !active ) { return {}; } return ",\"notes_total\":" + std::to_string( counts.total ) + ",\"notes_kept\":" + std::to_string( counts.kept ); } // R1 (terminality round A, verify-wave1) — the CEILING THIS BUNDLE WAS SHAPED AGAINST, in the dialect whose // whole audience is machines. H9 put budget_tokens= on the XML root and this twin never got it, so a --json // caller could see a bundle that had plainly been re-shaped (`capped`, a shorter sigs array) and could not // read WHICH ceiling did it, nor check est_tokens against the number it had itself passed. Present exactly // when the XML twin's is (an explicit --token-budget), absent otherwise — an attribute on a run where the // flag was inert would be its own false disclosure — and CHARGED like every other byte here, for the reason // the XML twin's H9 comment gives at length: a budget disclosure that pushed the bundle past its own budget // would be the funniest possible way to fail this finding. A free function over emitForLensJson's locals, // the forLensNotesStanza / forLensJsonTailStanza precedent directly above and below. inline std::string forLensJsonBudgetStanza( std::size_t tokenBudget ) { if( tokenBudget == 0 ) { return {}; } return ",\"budget_tokens\":" + std::to_string( tokenBudget ); } // #61 (redhat-et/ripwire#61, YogevKr, 2026-09-08) — THE OVER-CEILING PREDICATE for the XML dialect, and the // SECOND ceiling a `--for` root can name. Written as a free function for the reason its JSON sibling above // is: runForLens is one of the largest bodies in this file, and the rule this expresses is a contract of its // own rather than a step in that emitter. // // --max-tokens reaches this verb through cli.h's isForDetailBudget carve-out and shapes the BODIES // (runForLens's detailBodyBudget) — the header, signatures, legend and symbol table are never charged // against it — so `--for=Q --detail=30 --max-tokens=300` shipped `max_tokens="300"` beside // `est_tokens="2640"`: a ceiling named on a document 8.8x past it, in silence, while `` // disclosed the body cut honestly right beside it. // // THE PARTIAL APPLICATION STAYS; THE SILENCE DOES NOT. METHODOLOGY §9 #2 — "when a ceiling would cut // something above the cliff, compress first, move prose into attributes second, and if it still does not // fit, exceed the ceiling with over_ceiling="1" rather than drop the row that would have terminated the // search". Thirty small functions totalling ~3.4K tokens, COMPLETE, are the terminating answer; a rung that // trimmed them to fit 300 tokens would make the tool worse and would still have been perfectly honest. What // was missing is §9 #6, "a ceiling attribute names the ceiling actually applied": the VERDICT. So this is // the same rule, the same unit and the same attribute budget_tokens already answers to (packtask.h F2) — // over_ceiling="1" whenever est_tokens exceeds a ceiling THIS ROOT STATES. The default map has computed the // identical verdict since §F5 (main.cpp, maxTokensFit.isOverCeiling); this path had no equivalent, which is // the "as the default map does" the reporter's Expected behavior cites. // // CONVERGENCE is unchanged and for the unchanged reason: the attribute and its legend clause only ADD bytes, // so a document over EITHER ceiling stays over it and the caller's est_tokens fixpoint never oscillates. // forbudget-soft: the W3FIX ceiling ladder's last rung (ladderFired) went with the hard --token-budget ceiling, and the // --for callers pass tokenBudget 0 (budget_tokens= names a soft target) — max_tokens= is the ceiling this predicate labels. // Gate: test/formaxtokenscheck.sh. inline bool forLensOverCeiling( std::size_t tokenBudget, int maxTokens, bool hasBodyCeiling, std::size_t estTokens ) noexcept { return ( tokenBudget > 0 && estTokens > tokenBudget ) || ( hasBodyCeiling && maxTokens > 0 && estTokens > std::size_t( maxTokens ) ); } // PR #135 — THE PRICED ROOT, as a function of the header shape the ceiling ladder chose. Everything runForLens // splices onto its header after the ladder lives here, in its original order: the late root attributes and // clauses, the est_tokens= clause, then the est_tokens fixpoint with over_ceiling="1" and the clause defining it. // It is one function so the ladder can price the header it will EMIT, not the one it builds. The label rides // whenever est_tokens exceeds budget_tokens (F2), and est_tokens prices markup at 2.50 B/tok while the allowance // is 2.36 x 1.15 = 2.714 B/tok. So a bundle in that band carried 70 bytes the ladder never saw, and one it had // fitted within 70 B of the allowance shipped past it with no rung fired: estchargecheck #11 A7, 5 429 B against // 5 428 B (the sweep beside it reds the same defect on a git-less corpus at 12 of 52 budgets). // `measured` is ForLensBlockCharge::isMeasured: the direct-emission degrade path gets the splices but no number, because its // header goes to stdout before the bundle it would describe has been measured. // The DISCLOSE sink for runForLens's pre-rendered blocks. est_tokens= prices the whole bundle, so ANY block the charge // buffer could not measure — or a sibling (, , ) streamed directly and uncharged — leaves // it unmeasurable, and ForLensRootFinish::measured (this flag) then omits it rather than print a number that left // those bytes out. The sigs block also drops the auto bodies and the ladder, which key on sigsPreRendered itself. struct ForLensBlockCharge { enum class DisclosureWhy : std::uint8_t { SigsUnmeasured, // streams directly: no bundle measurement at all SiblingBlockUnmeasured, // // streams directly: its bytes are outside the charge }; bool isMeasured = true; void disclose( DisclosureWhy ) noexcept // every reason records the same fact { isMeasured = false; } }; struct ForLensRootFinish { std::string_view autoAttr, sigsCeilingAttr, capAttrs; // root attributes, before the first ">" std::string_view droppedPositiveNote, sigsCeilingNote, capNote; // clauses, before the last " -->" std::string_view sectionsStubNote; // L2 (round-1 lever B1): kForSectionStubLegend, present only when this run actually stubbed / // L3 follow-up (CodeRabbit 4053600616): notes.h's ONE marker — the attribute before ">" like its siblings below. Absent on a clean read. std::string_view notesDegradedAttr, notesDegradedNote; bool measured = false; std::string_view estTokensLegend, overCeilingLegend; std::size_t nonHeaderMarkupBytes = 0; // every markup byte outside the header, "" included std::size_t bodyTokens = 0; // the body-rate sections, already priced std::size_t tokenBudget = 0; int maxTokens = 0; bool bodyCeiling = false; // forbudget-soft: the default answer's ranked head passed the runaway guard (SigsCutReport::rowDropCut): over_ceiling="1". // Its reading (forRunawayGuardNote) rides the clauses (sigsCeilingNote) already — never kOverCeilingLegend, whose // est_tokens-versus-a-named-ceiling reading is not what happened. bool guardOver = false; }; // PR #215 review: the finished header AND the number it prints, handed back together. finishForLensHeader // below is this function's header half and stays the name every emit site calls; the ceiling ladder's exact- // ceiling test needs the OTHER half, because "does this document fit what its root promises" is the question // `est_tokens <= budget_tokens` asks and nothing else answers it. Computing it a second time beside this // fixpoint is how the two came to disagree on the RATE in the first place, so there is one fixpoint and two // readers of it. `estTokens` is the number the document will actually carry — the second-stage value when // over_ceiling="1" rides, since those bytes are part of the document the number prices. struct ForLensPricedHeader { std::string header; std::size_t estTokens = 0; // 0 on the unmeasured degrade path (`f.measured` false — no number is printed there) }; inline ForLensPricedHeader finishForLensHeaderPriced( std::string header, const ForLensRootFinish& f ) { // T3: the bundle=auto disclosure attributes, then budget_bytes= (its presence is decided by the sigs render), // then the INDEXING-cap attributes (root facts of the RANKING, so on the root est_tokens prices rather than in // the ladder's input). All three go in BEFORE est_tokens is computed, so the number measures a header that // already carries these bytes exactly. An attribute dropped by an unexpected shape costs nothing a reader can // be misled by: the auto section is still disclosed by its own element. See spliceBefore for the boundary. spliceBefore( header, ">", /*fromEnd=*/true, f.weak ? std::string_view( " weak=\"1\"" ) : std::string_view() ); // A2 dropped_positive="N" (absent at zero, the pr_converged precedent — never a fabricated "0"), then the // budget_bytes= clause, whose attribute rides only a trimmed , then the cap clause, which DEFINES its // attributes by carrying them verbatim (the legendcoveragecheck contract dropped_positive= also uses). spliceBefore( header, " -->", /*fromEnd=*/true, f.droppedPositiveNote ); spliceBefore( header, " -->", /*fromEnd=*/true, f.sigsCeilingNote ); spliceBefore( header, " -->", /*fromEnd=*/true, f.capNote ); spliceBefore( header, " -->", /*fromEnd=*/true, f.sectionsStubNote ); spliceBefore( header, " -->", /*fromEnd=*/true, f.notesDegradedNote ); // L3 follow-up (CodeRabbit 4053600616) if( !f.measured ) { return { std::move( header ), 0 }; } // N1: the legend clause defining est_tokens= goes into the LAST comment first, so header.size() below // already counts it exactly. spliceBefore( header, " -->", /*fromEnd=*/true, f.estTokensLegend ); // SELF-REFERENCE (the §L2 mechanism generalized): the est_tokens attribute is part of the document est_tokens // measures, so its own digit string belongs inside the byte total. Bounded 4-pass fixpoint, same shape and // bound as serialize()'s and buildRecall's; the attribute BUILT last is the attribute inserted. On convergence // (the `break`, reached in <=2 passes on every shape measured) the number stated is exactly the number the // document's own bytes were measured against; on the bound, a residual under one token — never fabricated. // EACH KIND OF BYTE AT ITS OWN RATE: markup (header, , , , , the mermaid graph // block, the tail, a compact ) at kBytesPerTokenDefault, and the --detail / auto bodies at the // kBytesPerTokenBody rate chargeSection already charged them at (one rate for both kinds is §H7's defect). const auto priceFixpoint = [ & ]( std::size_t markup, std::size_t overBytes ) { std::size_t est = rw::tokensForEmittedBytes( markup + overBytes, rw::kBytesPerTokenDefault ) + f.bodyTokens; std::string a = " est_tokens=\"" + std::to_string( est ) + "\""; for( int pass = 0; pass < 4; ++pass ) { const std::size_t next = rw::tokensForEmittedBytes( markup + a.size() + overBytes, rw::kBytesPerTokenDefault ) + f.bodyTokens; if( next == est ) { break; } est = next; a = " est_tokens=\"" + std::to_string( est ) + "\""; } return std::pair{ est, a }; }; auto priced = priceFixpoint( header.size() + f.nonHeaderMarkupBytes, 0 ); std::string overAttr; // F2 (capture-audit verify-wave2 2026-09-05): over_ceiling= is a PROPERTY OF THE EMITTED DOCUMENT, not of a // rung — over_ceiling="1" whenever est_tokens exceeds A CEILING THE ROOT NAMES (#61: forLensOverCeiling), on // every rung; absent means inside all of them. Decided from the fixpoint because its own 17 bytes are part of // the document the number prices, and in two stages because its DEFINITION is header bytes too: splicing the // clause unconditionally would charge every budgeted bundle for an attribute it does not carry. Monotone — // adding bytes only RAISES est_tokens — so one extra stage is exact and the flag never oscillates. The // definition rides the legend of the document that carries the attribute. const bool namedCeilingOver = forLensOverCeiling( f.tokenBudget, f.maxTokens, f.bodyCeiling, priced.first ); if( namedCeilingOver || f.guardOver ) { // each clause rides only where its reading is true: the est_tokens one when that predicate holds; the runaway guard's // reading rode the clauses already (sigsCeilingNote) — both when both hold spliceBefore( header, " -->", /*fromEnd=*/true, namedCeilingOver ? f.overCeilingLegend : std::string_view() ); overAttr = " over_ceiling=\"1\""; priced = priceFixpoint( header.size() + f.nonHeaderMarkupBytes, overAttr.size() ); } // N1: onto the root — the same ">" would terminate // the comment early). W3FIX M3: the hand-rolled '--' collapse scrubbed dashes and NOTHING else, so a C0 // byte or invalid UTF-8 in the task made xmllint reject the document and a '\n' put a raw newline outside // CDATA — xmlCommentText (serialize.h) is the ONE scrub for all three, shared with --pack-task / MCP // `for` / --exemplar / --from-trace. Byte-identical on control-free input. (L1: the route note no // longer rides in the comment — route= carries it verbatim, attribute-escaped by ctxRootOpen, and // the JSON sibling keeps the same RAW text.) const std::string taskNote = xmlCommentText( cfg.forTask ); int forTopN = cfg.packTopN > 0 ? cfg.packTopN : kForLensDefaultTopN; // F-03: the cap the MCP `for` twin reads too // Ranking-confidence disclosure (paper-shape lane; arXiv 2607.24882 — abstention/confidence is the // unsolved retrieval axis: a retriever must be able to tell the caller when its own ranking is not // trustworthy). DERIVED, NEVER SCORED: this is the SAME adaptiveCut gap statistic --adaptive cuts // at, computed once here (the --adaptive block below reuses it — one call, identical parameters, so // the disclosure and the cut cannot disagree). Disclosure only: nothing below reads `forCut` to // change what is served. The mapping to high/low happens after the relevance floor, where the final // served head size is known. const AdaptiveCut forCut = adaptiveCut( lensRank, 5, std::size_t( forTopN ), /*scanFullDistribution=*/true ); // T14 (docs/research/adaptive-short-query.md): a name-exact route against a large pool of // identically-named, unrelated symbols can clear the cliff on TIE-BREAK order rather than // relevance — see lexical.h isAdaptiveHomonymDecline for the measured threshold and why. Computed // from the SAME forCut above (no second scorer), read by both the --adaptive block below (whether // to actually decline the narrowing) and deriveForConfidence below (confidence must not say "high" // on a cliff it declined to trust). // // review rv-t14a.md finding 1 (HIGH): this used to read `!conceptualRoute`, a DIFFERENT question // (isConceptualRoute only recognizes "subtoken+body") whose negation is also true on the THIRD // route state, "no-route" (--no-route's struct default, packtask.h LensRanking::routeTag) — so // --no-route --adaptive mislabeled an un-routed ranking as name-exact and declined + fabricated a // same-name count on a genuine cliff. isNameExactRouteTag is the one predicate emitCandidates and // MCP `for` already used their own way; all three now share it (lexical.h). const bool forHomonymDecline = isAdaptiveHomonymDecline( isNameExactRouteTag( lr.routeTag ), forCut, 5 ); // --adaptive (lever 2): cut the returned set at the relevance CLIFF — the largest // relative score gap in [floor, ceiling] (Adaptive-k). A sharp query keeps few; a flat/broad query // (no knee) hits the ceiling and is kept as-is (cap-and-note). floor=5, ceiling=forTopN. The cut // narrows forTopN BEFORE packSignatures selects, so the emitted set is exactly the kept head. The // note is legible in the header so the reader knows WHY the set is the size it is. Without --adaptive, // forTopN is untouched → byte-identical output (golden neutral). std::string adaptiveNote; if( cfg.adaptive ) { const int ceil = cfg.packTopN > 0 ? cfg.packTopN : kForLensDefaultTopN; // scanFullDistribution=true: --for caps at 40, so a sharp query's real cliff often sits BELOW the // cap while the top-40 head is flat — a scan bounded at the cap finds no knee and keeps 40/40 // (the recorded "inert on --for"). Scan the RAW lexical (BM25) distribution BEFORE the cap so the // true cliff is seen, then clamp kept into [floor, ceiling]. lensRank IS the raw lexical score here // (subtoken+body or name-exact; --anchor's blend is opt-in and handled by keeping this the same call). // `forCut` above is exactly this call (same scores, floor, ceiling, full-distribution scan), // hoisted so the confidence disclosure derives from the statistic --adaptive acts on. const AdaptiveCut& ac = forCut; char nb[ 200 ]; if( forHomonymDecline ) { // decline to narrow: forTopN stays at `ceil` (the pre-cut default), never ac.kept — the // fallback IS the default top-K, not a smaller, equally-arbitrary slice of the same pool. // Stated in the ANSWER (this note), not only stderr — T14 requirement 1. rw::formatTo( nb, sizeof( nb ), " [adaptive: declined - {} symbols share this exact name; the " "score gap here is tie-break order, not relevance - kept the default top-{}]", ac.positiveHits, ceil ); } else { forTopN = int( ac.kept ); if( !ac.hitCeiling && ac.cliffRank < ac.kept ) { rw::formatTo( nb, sizeof( nb ), " [adaptive: kept {} of {} - sharp cliff at rank {} ({}% drop), clamped up to the floor of {}]", ac.kept, ceil, ac.cliffRank, ac.dropPct, ac.kept ); } else if( !ac.hitCeiling ) { rw::formatTo( nb, sizeof( nb ), " [adaptive: kept {} of {} - cliff at rank {}, {}% drop]", ac.kept, ceil, ac.cliffRank, ac.dropPct ); } else if( ac.positiveHits <= ac.kept ) { rw::formatTo( nb, sizeof( nb ), " [adaptive: kept {} of {} - only {} symbols matched this query (sharp query, short tail)]", ac.kept, ceil, ac.positiveHits ); } else { rw::formatTo( nb, sizeof( nb ), " [adaptive: kept {} of {} - no relevance cliff (broad query saturates the score); capped at the ceiling]", ac.kept, ceil ); } } adaptiveNote = nb; } // LB-A (r10 §5) — THE RELEVANCE FLOOR: shrink the quota past its zero-score tail rather than pad // with it. ADMISSION, NOT RANKING — no score moves, only where the (score desc, id asc) head // stops. Applied HERE because lensRank is final at this point and forTopN is the one knob every // consumer below reads. The measurement and the rest of the reasoning: serialize.h, // relevanceFloorCut. The note lands in the header comment beside --adaptive's. auto [ flooredTopN, floorNote ] = relevanceFloorCut( lensRank, forTopN ); forTopN = flooredTopN; // the mapping, the two derived strings, and the reasoning behind both live ONCE in // deriveForConfidence (above runForLens) — forTopN is final here (floor cut applied), which is // what the completeness ground needs. // THE BUNDLE'S RESOLVED SURFACE: the top-N ids by lensRank — the exact set selects. Three // consumers now: the S5-E HAS-A compose view, the B6.3 route view, and (§P3) the scope // filter, which keeps only interfaces this surface actually reaches. §B1.4 (capture-audit-4): // HOISTED above the --json branch — both dialects need it now, XML to RENDER lego/compose/routes, // JSON to COUNT them without rendering (see below). Computed once, kept alive for the // direct-emission degrade paths further down too. std::vector lensSurfaceIds; { const std::size_t S = ing.symbols.size(); lensSurfaceIds.resize( S ); for( NodeId i = 0; i < S; ++i ) { lensSurfaceIds[i] = i; } // A4-F23c: score-only sort left ties straddling the cut stdlib-dependent. Use the (score desc, id // asc) total order (same key packSignatures selects with) so the surface set is deterministic. rw::sortutil::radixSortByScoreDescId( lensSurfaceIds, lensRank ); const std::size_t cap = std::min( std::size_t( forTopN ), S ); lensSurfaceIds.resize( cap ); } ForConfidence forConf = deriveForConfidence( forCut, forTopN, forHomonymDecline ); // ── THE BUDGETED-BUNDLE CANDIDATE PAGE (paging PoC, issue #294): budget + explicit window ⇒ the // resumable candidate page (shared inline; the un-paged bundle below stays byte-identical). if( cfg.tokenBudget != 0 && ( cfg.pageLimit > 0 || cfg.pageOffset > 0 ) ) { // W3-N1/#294 review: the page redacts through packSignatures — the tally must be reported, // exactly like the --json early return below (a redacted credential must never be silent). const std::string rankFlags = forPageRankFlagArgv( cfg ); const int pageRc = emitForCandidatePage( ing, lensRank, forCut, rw::ForCandidatePageReq{ cfg.forTask, "--for=", rankFlags, /*pasteHandle=*/true, /*compactLegend=*/cfg.legend != "full", routeNoteRaw, flRootArg, redactPtr, flSingleRoot ? std::string_view( gitstamp::stampAt( std::string( root ) ) ) : std::string_view(), cfg.tokenBudget, cfg.pageLimit, cfg.pageOffset } ); reportRedactions( stderr, redactCounts ); // W3-N1: the page redacts — the tally reports return pageRc; } // L-W: coverage= rides the SAME sentence and the SAME byte exemption as confidence=/margin_pct= — but ONLY // on a THIN answer (owner decision 2026-09-12: present-only). The thin verdict is decided HERE, from the // resolved surface above and the top symbol's term share, and it drives three things at once: the // attribute, its legend clause (present-only, so the clause never explains an absent attribute) and the // r=1 row's next= (the widening page instead of the body). A confident answer carries none of them and is // byte-identical to the pre-L-W bundle. Nothing scored ⇒ no top symbol ⇒ thin by the files clause, and no // coverage= to print (a fabricated 0 is what non-negotiable #3 forbids). const int forCoverage = forCoveragePct( lr.evidence, topLensId( lensRank ) ); const bool forThin = forAnswerIsThin( forCoverage, distinctFilesOf( ing, lensSurfaceIds ) ); const bool forCoverageOn = forThin && forCoverage >= 0; if( forCoverageOn ) { forConf.attrs += " coverage=\"" + std::to_string( forCoverage ) + "\""; forConf.note += kForCoverageLegend; } // The confidence pair's own bytes, captured BEFORE the budget clause below is appended — see the // charging note there for why the two disclosures are charged differently. const std::size_t confidenceOwnBytes = forConf.attrs.size() + forConf.note.size(); // P1 (L7): the two halves separately, captured at the SAME early point — the compact dialect emits its own // short clause (kForCompactLegendConfidence) in place of the early note, and the exemption below must // subtract what that dialect emitted, never the appended budget clause (which is charged, see below). const std::size_t confidenceEarlyAttrsBytes = forConf.attrs.size(); const std::size_t confidenceEarlyNoteBytes = forConf.note.size(); // H9 (capture-audit 2026-09-04): the CEILING this bundle was shaped against, named on the root. // `--for --token-budget=1500` re-shapes the whole answer — the root goes `bundle="compact" // bodies="0" reason="budget"` — and the audit found that N itself appeared nowhere, so a reader // could see THAT a budget cut the bundle but never WHICH one, and could not tell a 1500-token cut // from a 15,000-token one. `--pack-task`, the sibling that takes the same flag, has always printed // `budget_tokens=`; this is the same attribute, on the verb that was missing it. `--max-tokens` is // read by --for only under --detail=N (cli.h's kShapingVerbs carve-out), so `max_tokens=` is // charged to exactly that shape and absent otherwise — an attribute that appeared on a run where // the flag was inert would be its own false disclosure. // // CHARGING, and why it splits from the confidence pair's. Both ride forConf, but only the // confidence half is EXEMPT from the byte budget (confidenceExemptBytes is taken from // confidenceOwnBytes above, captured before this append): confidence= must survive a tight ceiling // because it qualifies the ANSWER — the fordisclosurecheck precedent. These two attributes qualify // the BUDGET, and a budget disclosure that made the bundle exceed its own budget would be the // funniest possible way to fail this finding. So they are charged like any other bytes, and the // clause is written terse for the reason forLensHeaderText's W3-S note records at length: this // header has a MEASURED hard byte constraint (fornotesbudgetcheck), where 150 bytes of prose is // 2% of a 1550-token ceiling. Gate: test/budgetpolicycheck.sh arm (B). const bool forGateBudget = cfg.tokenBudget > 0; const bool forEndLines = rw::endLinesFitCeiling( forGateBudget ? std::size_t( cfg.tokenBudget ) : 0u, forBodyCeiling ); // e= rides? if( forGateBudget ) { forConf.attrs += " budget_tokens=\"" + std::to_string( cfg.tokenBudget ) + "\""; } // round C: the body ceiling's two disclosures (this attribute and its note below), measured so a soft target's cut can // be priced without them — a body ceiling never moves a row (formaxtokenscheck (E)) std::size_t forBodyCeilingDisclosureBytes = 0; if( forBodyCeiling ) { const std::size_t before = forConf.attrs.size(); forConf.attrs += " max_tokens=\"" + std::to_string( cfg.maxTokens ) + "\""; forBodyCeilingDisclosureBytes += forConf.attrs.size() - before; } // forbudget-soft (owner decision 2026-10-14): budget_tokens= is a SOFT target now — rows stop once it is reached and // nothing else is shaped by it — so its reading says so (kForSoftBudgetNote); max_tokens= stays the hard body ceiling. // No "--" in these strings: they ride inside an XML comment, where a double hyphen is ill-formed (G4). The flags are // named without their dashes for exactly that reason. if( forGateBudget ) { forConf.note += rw::kForSoftBudgetNote; } if( forBodyCeiling ) { const std::size_t before = forConf.note.size(); forConf.note += " [max_tokens=: the token ceiling the detail bodies were shaped against]"; forBodyCeilingDisclosureBytes += forConf.note.size() - before; } // §L10b: mention_anchored=/doc_mentions= — the machine form of mentionNote/docMentionNote, EACH // present only when its own note fired (unlike confidence=/at=, these are not facts of every // ranking — a query that anchors or surfaces no docs carries neither). Same lr.anchorLifts the // candidates root's anchored= already reads (§A4f); lr.docMentionCount is new (§L10b, packtask.h). // Both gates read the COUNTS, not the note strings — mention_anchored="0" is the fabricated zero // non-negotiable #3 forbids, and a note can now be non-empty on a run that anchored nothing. std::string mentionDocAttrsStr; if( lr.anchorLifts > 0 ) { mentionDocAttrsStr += " mention_anchored=\"" + std::to_string( lr.anchorLifts ) + "\""; } if( lr.docMentionCount > 0 ) { mentionDocAttrsStr += " doc_mentions=\"" + std::to_string( lr.docMentionCount ) + "\""; } // The INDEXING caps do NOT join this string — they are spliced onto the root after the sigs ladder, // where budget_bytes= goes and for its reason (see there). // M10: --for reads git for the per-file churn= column (folded onto the bundle below, mined once in // main.cpp's resolveHistoryWalk pass) and, before this fix, carried no anchor — an agent // quoting churn= into a handoff had nothing checkable to pin it to. Single-root only (same gate // flRootArg itself uses): "" on multi-root or a non-git root, same silence-means-omitted convention // every other stamped verb follows. ALWAYS present when non-empty, like confidence= — a root fact, // never budget-dropped, which is why it rides the SAME byte-exempt reserve as confidence= below. // Computed once per RUN, in runForLens (both dialects need it: XML wraps it as an attribute, JSON quotes // it bare), so a --token-budget run that renders in up to three payment modes pays the stamp's two git // subprocesses once, not once per render (CodeRabbit on #383, verbs_for.h:3843; test/forrankordercheck.sh (10c)). const std::string forAtAttrStr = forAtStamp.empty() ? std::string() : ( " at=\"" + forAtStamp + "\"" ); // R2-AF (round 2, S4): resolved once, ahead of the header build below, so its legend clause can be // present-only in BOTH dialects. The rows are RENDERED here too (forHdrXml) and emitted, as that same // string, right after headerStr is flushed and before . PRICED like every other emitted section // (CodeRabbit 4054594308): its bytes join est_tokens (rootFinish.nonHeaderMarkupBytes), both halves of // the ceiling ladder, and the residual the --detail bodies, the auto enrichment and the explicit-regime // tail are sized from. Rendered after the ceiling decisions they used to be missing from every one of // those sums, so a task naming several long paths printed an est_tokens that left out ~1.6 KB of rows. // NOT charged against the trim allowance (fixedBytes/sigsBudget below), which keeps the ranked // head byte-identical with and without the rows and identical to the MCP twin's (mcpverbs.h forTaskText, // whose sig ledger does not see them either): a lookup the task asked for by name is not paid for in // ranked rows. What bounds it is forNamedHeaderRows' own ENSURES(out.size()<=allNamed.size()) // (mention.h): at most one row per distinct file the task text names, deduplicated. The "+365 B" figure // is this ROUND's own measurement rule (read by the external eval/scoring harness that grades this // round, not by ripwire) and MUST NOT be enforced here: a runtime byte cap on these rows could drop a // correct row or make a correct answer wrong, which owner policy forbids a cap from doing (caps are // blow-up guards only). Not part of the --json dialect (that branch returns above the emission); the // extra resolve and render on a --json call are cheap and unused, not wrong. const std::vector forHdrRows = rw::forNamedHeaderRows( ing, cfg.forTask ); const std::string forHdrXml = renderForHdrRowsXml( ing, forHdrRows, flRootArg ); // HEAD ROUND-ROBIN (serialize.h reorderHeadRoundRobin): the routed subtoken+body answer to a question that names no // indexed file. Only the shown rows reorder: the fold and the hops keep walking lensSurfaceIds in score order — // measured on the rank-gates 44 rows (lane hrr report): hops and fold that followed the reordered head lost two graded // ✓ rows (their strongest rows' callees and bodies left the hop window); in score order they lose none. const bool forHeadRR = rw::forHeadRoundRobinOn( conceptualRoute, rw::taskNamesIndexedFile( ing, cfg.forTask ), cfg.tokenBudget > 0 ? std::size_t( cfg.tokenBudget ) : 0u, forBodyCeiling ); // H1 (B0 r2): the bundle is emitted under a GLOBAL payload budget (serialize.h kForPayloadBudgetBytes; an // EXPLICIT --token-budget=N overrides it at the same conservative byte rate the --max-tokens fitter uses). // Trimming happens inside only, so the header is built as a string and the sibling blocks (lego, // compose) are rendered FIRST — their exact byte cost is subtracted from the bundle budget before // packSignatures enforces the remainder. Emission ORDER is unchanged (header, sigs, lego, compose, bodies, // ): when nothing trims, the output is byte-identical to the pre-H1 path. W3FIX H2: the header goes // through forLensHeaderText (above) rather than being appended once, because the ceiling ladder below has // to PRICE a header without the comment's task echo or without route= and then emit that exact shape. const std::string rootOpenStr = ctxRootOpen( cfg.forTask, routeNoteRaw, flRootArg ); // T3 (pre-registered: docs/EVALS.md §4, T3 round): terminal-by-default — the auto mode is on // unless the caller took an explicit body posture (--detail=N) or opted out (--signatures-only). The // --json and --format=candidates dialects never reach the auto machinery (candidates returned above; // --json returns before it below), so this mode is an XML-bundle fact only. const bool autoBundleMode = cfg.detail == 0 && !cfg.signaturesOnly; // COMPACT conceptual serving (docs/EVALS.md, the T3 route-narrowing round) — see planForEnrichment. const ForEnrichmentPlan plan = planForEnrichment( autoBundleMode, conceptualRoute, cfg.autoBodies ); // NOT const: the enrichment block below may drop the LEGEND (autoBundle/compactBundle=false) and // rebuild the header without it — the ladder's later rebuilds read this struct through // buildForHeader and must honor that decision. One cause reaches it now: the chargeSection degrade // (surfaceOff — no attribute either); forbudget-soft retired the exhausted-ceiling switch (a soft target // keeps the legend and, on the compact route, serves the hops cut marker instead — hopsCutMarker below). // A tight explicit budget never turns the disclosure off — test/fordisclosurecheck.sh. // PR #215 review: does any row this bundle could serve carry sc=? Both dialects' sc= readings are // present-only on this, and the dropped-legend note names sc= only when there was a reading to lose. // Decided from the RANKED SET and before the header is built, because the un-budgeted path ships the // header built here and never rebuilds it. Deliberately an OVER-approximation: the byte ladder may still // trim the only scoped row, and a reading with nothing to define costs 29 B, while the reverse costs a // reader an attribute with no definition anywhere in the document. bool forScPresent = false; for( std::size_t i = 0; i < ing.symbols.size() && !forScPresent; ++i ) { forScPresent = lensRank[i] > 0 && rw::hasScopeAttr( ing.symbols[i] ); } // packCompose emits an edge whose owner or member type is on the lens surface, a subset of the ranked set. const auto isRanked = [ & ]( rw::NodeId id ) noexcept { return id < lensRank.size() && lensRank[ id ] > 0; }; const bool forComposePresent = std::ranges::any_of( g.composeEdges, [ & ]( const rw::ComposeEdge& ce ) { return isRanked( ce.ownerSym ) || isRanked( ce.typeSym ); } ); // TRAIN 9: the same shape for the two attributes the merged stack put on a default first screen. Both are // over-approximations by design — a clause with nothing to define costs a reader one clause, an undefined // first-screen attribute costs them the meaning (the scPresent note above states the trade). bool forLegoPresent = false; bool forLayerPresent = false; for( std::size_t i = 0; i < ing.symbols.size() && !( forLegoPresent && forLayerPresent ); ++i ) { if( lensRank[i] <= 0 ) { continue; } if( i < g.implementors.size() && !g.implementors[i].empty() ) { forLegoPresent = true; } if( *rw::builtinLayer( rw::rootRelPath( ing, ing.symbols[i].fileId ) ) != '\0' ) { forLayerPresent = true; } } // #60: the same shape again, and READ FROM THE RANKED SET like its three siblings above. The first // draft tested the whole symbol table on the theory that a hop's owner need not be ranked; that // was simply wrong — the compact bundle's hop candidates ARE the positive-score head of the ranked // surface (buildForCompactHops), so an owner reaches this document as a row or as a row of // one, and both are ranked. Corpus-wide cost ~40 tokens of clause on EVERY --for answer from any // tree holding one top-level call (this repo; 72% of vue-core's files), which is the mirror-image // waste of the rule this header states: a legend that defines something the document did not emit. // #60: THE ROW SET, not the ranked set — and this one is deliberately NOT its siblings' shape. // `scPresent`/`legoPresent`/`layerPresent` test rank>0 corpus-wide and let the over-approximation // fall the safe way, which costs a clause nobody needed. This clause is ~154 B (compact) / ~250 B // (full) on the tool's most-used verb, and rank>0 is true for a module-scope owner on any tree // holding one top-level call — every --for answer on THIS repository paid it, with no owner row in // any of them (measured over six queries). So it reads the rows the document can actually carry: // `lensSurfaceIds` is the rank-ordered surface, `` rows are its top-forTopN head (the adaptive // cut has already narrowed forTopN here, "so the emitted set is exactly the kept head") and the // compact `` rows are its rank>0 head capped at kPackTaskBodyCandidates — so the union is one // head walk. What is left over-approximating is only the H1 BYTE ladder trimming inside that head, // which is the residual a header built before its payload cannot avoid. const std::size_t forRowHead = std::max( std::size_t( forTopN > 0 ? forTopN : 0 ), rw::kPackTaskBodyCandidates ); bool forModScopePresent = false; bool forEndLinePresent = false; for( std::size_t i = 0; i < lensSurfaceIds.size() && i < forRowHead && !( forModScopePresent && forEndLinePresent ); ++i ) { const rw::NodeId sid = lensSurfaceIds[i]; if( lensRank[sid] <= 0.0f ) { continue; } forModScopePresent = forModScopePresent || ing.symbols[sid].kind == rw::SymKind::ModuleScope; forEndLinePresent = forEndLinePresent || ( forEndLines && rw::mayCarryEndLine( ing.symbols[sid] ) ); } // FE-B: may a row of this bundle carry via="name"? Only the sections that render can, and each one's // node set is known before the header: the compact route's hop ids, T3's auto-body candidates, or --detail's head // (autoBundleMode excludes --detail). Their name-only out-edges decide it — over-approximating only the byte ladder // and the 16-per-symbol cap inside those sections, never the rows the header itself lists. // owner-hop: THE QUESTION'S OWNER plan (forOwnerHopPlan), decided here, before the header, because the header's // qword clause and its via="name" question both read it; the compact route's renders exactly this plan. const ForOwnerPlan ownerPlan = ( autoBundleMode && plan.compact ) ? forOwnerHopPlan( ing, g, cfg.forTask, lensSurfaceIds, lensRank, forCompactHopIds( lensSurfaceIds, lensRank ) ) : ForOwnerPlan{}; // for-body-slot: THE ONE OWNER BODY of an explanatory question (forowner.h forBodySlotOwner), decided here for the same // reason as the owner plan above: the header's slot clause reads it. The compact route only (an XML answer whose caller // chose no body posture: autoBundleMode excludes --detail/--signatures-only, plan.compact excludes --auto-bodies and the // name-exact route, which already serves the anchor's body). const rw::ForBodySlotOwner slotOwner = ( autoBundleMode && plan.compact && !cfg.json && rw::forBodySlotAsked( cfg.forTask ) ) ? rw::forBodySlotOwner( ing, g.outOff, g.outTargets, cfg.forTask, lensSurfaceIds, lensRank ) : rw::ForBodySlotOwner{}; bool forViaPresent = false; bool forViaByOwnerOnly = false; // owner-hop: the six ranked hop ids alone answer no, the owner rows switch it on bool forSplitPresent = false; // attrib-multidef Q1: the same question for prov="split", over the same ids if( !g.outNameOnly.empty() || !g.outProv.empty() ) { std::vector viaIds; if( autoBundleMode && plan.compact ) { // only the hops packHops gives a slot can carry a row (the hop-slot rule drops a hop whose every callee // edge is name-only, so its via="name" rows never print) const auto unslotted = [ & ]( rw::NodeId id ) { return !rw::hopSlotHasProvenEdge( g.outOff, g.outNameOnly, id ); }; viaIds = forCompactHopIds( lensSurfaceIds, lensRank ); std::erase_if( viaIds, unslotted ); const bool rankedVia = rw::namesOnlyOutAny( g.outOff, g.outNameOnly, viaIds ); // the question without owners std::vector ownerViaIds = ownerPlan.rowIds; // owner rows carry too std::erase_if( ownerViaIds, unslotted ); forViaByOwnerOnly = !rankedVia && rw::namesOnlyOutAny( g.outOff, g.outNameOnly, ownerViaIds ); viaIds.insert( viaIds.end(), ownerViaIds.begin(), ownerViaIds.end() ); } else if( autoBundleMode && plan.autoBodies ) { viaIds = computeAutoBodyCandidateIds( ing, lensSurfaceIds, lensRank, routeAnchorDefs ); } else if( cfg.detail > 0 ) { viaIds = forDetailIds( ing, lensRank, cfg.detail, forTopN ); } forViaPresent = rw::namesOnlyOutAny( g.outOff, g.outNameOnly, viaIds ); forSplitPresent = rw::splitArmsOutAny( g.outOff, g.outNameOnly, g.outProv, viaIds ); } ForLensHeaderParts headerParts{ cfg.forTask, rootOpenStr, taskNote, adaptiveNote, mentionNote, boostNote, docMentionNote, sibliftNote, expandNote, floorNote, forConf.attrs, forConf.note, forAtAttrStr, mentionDocAttrsStr, cfg.anchor, plan.autoBodies, plan.compact, cfg.legend == "compact", /*tailLegend=*/true, /*idRouteLegend=*/true, flRootArg, /*hdrLegend=*/!forHdrRows.empty(), forScPresent, forComposePresent, forLegoPresent, forLayerPresent, forModScopePresent, forEndLinePresent, forViaPresent, ownerPlan.any(), forViaByOwnerOnly, slotOwner.any() }; headerParts.splitPresent = forSplitPresent; // attrib-multidef Q1 (a trailing member: the aggregate above keeps its order) // body-fold (forfold.h): the bodies of the top ranked rows, appended — decided here because the header's fold clause reads it. // The compact route's XML answer in the DEFAULT regime only: an explicit --token-budget is a size the caller named (that // answer stays byte-identical), and autoBundleMode already excludes --detail/--signatures-only/--auto-bodies. --max-tokens is // NOT read here (FIX1 B1: --for's notice says this answer ignores it — the detail bodies' ceiling — so it must not turn the // fold off either; shapingflagcheck (A)/(F)). const std::vector foldIds = ( autoBundleMode && plan.compact && !cfg.json && cfg.tokenBudget <= 0 && rw::forBodyFoldOn() ) ? rw::forBodyFoldCandidates( ing, lensSurfaceIds, lensRank, std::size_t( std::max( forTopN, 0 ) ), slotOwner.id ) : std::vector{}; headerParts.bodyFoldPresent = !foldIds.empty(); // mf1-nx (forcallerhop.h): the callers of the ONE function a name-exact answer bodies — decided here because the header's // clause reads it. The name-exact route's auto-bodies XML answer in the DEFAULT regime only, as the fold: an explicit // --token-budget is a size the caller named (that answer stays byte-identical), and autoBundleMode excludes --detail, // --signatures-only and --auto-bodies on the compact route (plan.autoBodies is the name-exact route's bundle). const std::vector callerHopIds = forCallerHopIds( cfg, ing, g, lensSurfaceIds, lensRank, routeAnchorDefs, autoBundleMode && plan.autoBodies && isNameExactRouteTag( lr.routeTag ) ); headerParts.callerHopPresent = !callerHopIds.empty(); // mf1-nx wave 4 (fordispatchhop.h): the task-named calls a body candidate made through an interface receiver — the caller // hop's posture (name-exact auto bodies, default regime, XML), decided here because the header's clause reads it const std::vector dispatchSites = ( autoBundleMode && plan.autoBodies && isNameExactRouteTag( lr.routeTag ) && !cfg.json && cfg.tokenBudget <= 0 && rw::forDispatchHopOn() ) ? rw::forDispatchSites( ing, g, computeAutoBodyCandidateIds( ing, lensSurfaceIds, lensRank, routeAnchorDefs ), cfg.forTask ) : std::vector{}; headerParts.dispatchHopPresent = !dispatchSites.empty(); // ep-manifest (epmanifest.h): the manifest entry-point groups that register a row of the ranked head — rendered here // because the header's clause reads it. The default regime and XML only, as the fold and the caller hop. rw::EpRender epManifest; std::vector epHead; // the ranked head the clause was priced for; narrowed to the rows emits once it renders // manifest-noncode (listedin.h): the manifest lines listing a file of that same ranked head, same regime, same re-pick rw::ListedRender listedIn; const bool peerManifestRegime = autoBundleMode && !cfg.json && cfg.tokenBudget <= 0; if( peerManifestRegime && ( rw::forEpManifestOn() || rw::forListedInOn() ) ) { for( std::size_t i = 0; i < lensSurfaceIds.size() && i < std::size_t( std::max( forTopN, 0 ) ); ++i ) { if( lensSurfaceIds[i] < lensRank.size() && lensRank[ lensSurfaceIds[i] ] > 0.0f ) { epHead.push_back( lensSurfaceIds[i] ); } } if( rw::forEpManifestOn() ) { epManifest = rw::renderForEpManifest( ing, epHead ); } if( rw::forListedInOn() ) { listedIn = rw::renderForListedIn( ing, epHead, cfg.forTask ); } } headerParts.epPresent = !epManifest.xml.empty(); headerParts.listedPresent = !listedIn.xml.empty(); const auto buildForHeader = [ & ]( bool withRouteAttr, bool withTaskEcho, std::string_view extraNotes ) { return forLensHeaderText( headerParts, withRouteAttr, withTaskEcho, extraNotes ); }; std::string headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); // Q3 quality lens: fold churn (per-file) + clone-membership (per-symbol) onto the --for bundle, next // to the ccx/tested/amp already computed above. A4-P6: churn (12-month) was already computed above in // the SAME 18-month co-change popen (gitCoChangeAndChurn), so there is no second git subprocess here. // git-less ⇒ forChurn stays all-0 → churn= omitted. Guard sizing defensively (always sized above). if( forChurn.size() != ing.files.size() ) { forChurn.assign( ing.files.size(), 0u ); } // clone membership: 1 if the symbol is in any duplicate-clone group (same threshold as --clones default). std::vector forClone( ing.symbols.size(), 0u ); for( const CloneGroup& cg : findClones( ing, 40 ) ) { for( NodeId m : cg.members ) { if( m < forClone.size() ) { forClone[m] = 1u; } } } // IS-A: socket → bricks — for the interfaces THIS task actually reaches (§P3: the implementors map is // pre-scoped to lensSurfaceIds; withPaths keeps two same-named impls apart, exactly as --lego=TYPE // spells them). No interface reached ⇒ no element. Kept alive for the §P3×§P4 narrowing below // (XML render) and the §B1.4 count (JSON) — this is pure membership bookkeeping, never a redacting // seam, so hoisting it above the --json branch changes no dialect's redaction tally. std::vector> legoScoped = legoImplementorsOnSurface( ing, g.implementors, lensSurfaceIds ); // L-W (L-N): the r=1 row's next= names the file-grain widening page on the THIN verdict decided beside the // header above, and the body otherwise; "" keeps the --expand hint. const std::string forTopRowNext = forThin ? forWidenNext( cfg.forTask ) : std::string(); // DEEP-TAIL d2, FOLD C (forbudget-soft round C): the file-grain tail is page 0 of the --for file page — one shared // model (forpage.h computeFileTail) for both dialects and the MCP twin, from the SAME ranking and term evidence the // `--limit=40` page uses, so the tail, its next= page and the page itself cannot disagree. const FileTail forFileTail = computeFileTail( ing, lensRank, lr.evidence, lensSurfaceIds, flRootArg, cfg.forTask ); // the DEGRADE paths' tail // H2H-Graft lane 2 (2026-09-07): the tail's rank rows exclude only the files of the sigs rows actually EMITTED (a row the // ladder trimmed must land in the tail, never nowhere). std::vector shownSigIds; // L2: --json — the ranking ("sigs") bundle, plus (§B1.4) a COUNT of what // // would have held on this same surface. They still stay XML-only — rendering them for real would // mean a second redaction pass over interface method bodies the JSON dialect otherwise never takes — // but a reader can now tell "0, genuinely nothing here" from "dropped, ask for the XML dialect", // the notes_total precedent applied verbatim. jsonUnsupportedVerb already refused // --format=candidates/columnar/--detail, so reaching here with cfg.json means the plain lens bundle. if( cfg.json ) { // §B1.3: --with-graph has no JSON dialect yet (the mermaid block is XML-only) — warn once and // continue without it, the same warn-and-continue shape --partition's --with-graph note (below, // runPackTask) already uses, rather than silently dropping the flag's effect with no tell at all. if( cfg.withGraph ) { rw::emitRaw( stderr, "ripwire: --with-graph is not applied under --json (the mermaid graph block is XML-only for now) — emitted without it\n" ); } std::size_t legoTotal = 0; for( const std::vector& impls : legoScoped ) { if( !impls.empty() ) { ++legoTotal; } } std::vector onSurfaceFlags( ing.symbols.size(), 0 ); for( NodeId sid : lensSurfaceIds ) { if( sid < onSurfaceFlags.size() ) { onSurfaceFlags[sid] = 1; } } const auto isOnSurface = [ & ]( NodeId sid ) noexcept -> bool { return sid < onSurfaceFlags.size() && onSurfaceFlags[sid] != 0; }; // mirrors packCompose/packRoutes' own inSet membership test (serialize.h) verbatim, minus the // escaping/redaction/XmlWriter machinery — a COUNT, never a render, so it never touches redactCounts. std::size_t composeTotal = 0; for( const ComposeEdge& ce : g.composeEdges ) { if( ce.ownerSym < ing.symbols.size() && ( isOnSurface( ce.ownerSym ) || isOnSurface( ce.typeSym ) ) ) { ++composeTotal; } } std::size_t routesTotal = 0; for( const RouteEdge& re : g.routeEdges ) { if( isOnSurface( re.fromSym ) || isOnSurface( re.toSym ) ) { ++routesTotal; } } const int jsonRc = emitForLensJson( stdout, forLensJsonHeader( cfg.forTask, ForLensNotes{ routeNoteRaw, mentionNote, boostNote, sibliftNote, expandNote, docMentionNote, lr.anchorLifts, lr.docMentionCount, adaptiveNote, floorNote, forConf.level, forConf.marginPct, forCoverageOn ? forCoverage : -1, forWeak, forAtStamp, // abstention round 2: forCut is the SAME // cut the confidence facts above derive // from, so the counts and the verdict // cannot disagree. lensRank is per-symbol // over the whole index, so its size IS the // scored corpus. forCut.kept, forCut.positiveHits, lensRank.size(), lr.capJson } ), ForLensJsonInputs{ ing, lensRank, forTopN, fanInPtr, impurePtr, &forChurn, &forClone, testedPtr, ampPtr, redactPtr, cfg.packBudgetBytes, cfg.tokenBudget, notesPtr, legoTotal, composeTotal, routesTotal, flRootArg, &forFileTail, &lr.evidence, d.notesDegraded, forDocsAfterCode, forHeadRR, cfg.forTask, cfg.packTopN, forRankShapingFlags( cfg ) } ); // §B0: this early return skipped the end-of-function tally below, so a --for --json run redacted // SILENTLY — the one stderr line that tells the user a secret was in their tree never appeared. reportRedactions( stderr, redactCounts ); return jsonRc; } // H1: render lego + compose into memory first (they are independent of ), so the sigs // budget below is EXACT. open_memstream failure (ENOMEM-class) degrades to direct emission // after the sigs — same bytes, the budget just can't see the sibling blocks' size then. std::string legoStr, composeStr, routeStr, sigsStr; bool legoPreRendered = false, composePreRendered = false, routePreRendered = false, sigsPreRendered = false; // ONE pre-render for the four blocks below: into a charge buffer, and into `into` only when the buffer finished // whole. false means the open failed or a write was lost inside the buffer (rw::MemoryStream::finish), and the // direct-emission path further down renders that block straight to stdout, whole — the budget just cannot see it. // A false return also restores the run's redaction tally to what it was before the render: the direct path // renders the block again, and a secret it redacts must be counted once, not once per rendering. // The caller discloses a false return through `blockCharge` (ForLensBlockCharge), so each block keeps its own message. const auto preRender = [ & ]( const auto& render, std::string& into ) -> bool { const std::optional redactBefore = redactPtr != nullptr ? std::optional( *redactPtr ) : std::nullopt; const auto restoreRedact = [ & ]() noexcept { if( redactBefore ) { *redactPtr = *redactBefore; } }; rw::MemoryStream stream; std::FILE* const buffer = rw::openChargeStream( stream ); if( buffer == nullptr ) { restoreRedact(); return false; } render( buffer ); const rw::MemoryStreamBytes block = stream.finish(); if( !block.isWhole ) { restoreRedact(); return false; } into.assign( block.bytes ); return true; }; ForLensBlockCharge blockCharge; // a sibling block the budget could not see: est_tokens= is then omitted, not wrong // L2' (round-2 lever, ported from lane/r1-for-sections-stub): the stub's total= — each renderer's OWN // pre-cap row count, written by the SAME call that renders the real body (never a second, drifting // tally). Overwritten below if the §P3×§P4 narrow-and-re-render fires, so it always reflects the set // that would actually be restored. std::size_t legoPreCapCount = 0, composePreCapCount = 0; legoPreRendered = preRender( [ & ]( std::FILE* lm ) { packLego( lm, ing, legoScoped, lensRank, 12, redactPtr, impurePtr, kNoNode, /*withPaths=*/true, flRootArg, rw::LegoTargetedArgs().withDecidedOutside( g.baseOutsideClauses ), &legoPreCapCount, &g.implementors ); }, legoStr ); if( !legoPreRendered ) { DISCLOSE( blockCharge, ForLensBlockCharge::DisclosureWhy::SiblingBlockUnmeasured, "main: open_memstream failed (or its buffer lost a write) for the lego block — budget will not see its size" ); } if( !g.composeEdges.empty() ) { composePreRendered = preRender( [ & ]( std::FILE* cm ) { packCompose( cm, ing, g.composeEdges, lensSurfaceIds, &composePreCapCount ); }, composeStr ); if( !composePreRendered ) { DISCLOSE( blockCharge, ForLensBlockCharge::DisclosureWhy::SiblingBlockUnmeasured, "main: open_memstream failed (or its buffer lost a write) for the compose block — budget will not see its size" ); } } else { composePreRendered = true; // nothing to emit } if( !g.routeEdges.empty() ) { // B6.3: route view for the same relevant symbol set (top-N by lensRank) routePreRendered = preRender( [ & ]( std::FILE* rm ) { packRoutes( rm, ing, g.routeEdges, lensSurfaceIds ); }, routeStr ); if( !routePreRendered ) { DISCLOSE( blockCharge, ForLensBlockCharge::DisclosureWhy::SiblingBlockUnmeasured, "main: open_memstream failed (or its buffer lost a write) for the routes block — budget will not see its size" ); } } else { routePreRendered = true; // nothing to emit } // the bundle budget: default kForPayloadBudgetBytes; an explicit --token-budget beats it const std::size_t bundleBudget = cfg.tokenBudget > 0 ? rw::budgetBytesForTokens( std::size_t( cfg.tokenBudget ) ) : rw::kForPayloadBudgetBytes; // D2 (audit regressions, 2026-08-08): the adaptive note's own bytes are EXEMPT from the trim // charge (headerStr contains adaptiveNote verbatim, so subtracting its size prices the header as the // plain run's). --adaptive's contract is that only its CUT changes the emitted set ("without it, // output is byte-identical"); charging the note made a NO-CUT (flat) query drop one whenever the // corpus path length landed the trim boundary inside the note's ~110 B — adaptivecheck PHASE3's // "kept 40 of 40" header over a set one row short of the plain run's, i.e. a disclosed-inert mode // that was not inert. The note is still real bytes everywhere it matters downstream: est_tokens and // the ceiling ladder measure the emitted header, so nothing under-reports; only the global default // budget can overshoot, by at most the note (~0.5% of kForPayloadBudgetBytes), disclosed here. // T3: the bundle=auto legend's bytes are EXEMPT from the trim charge, exactly like the // adaptive note above (the D2 precedent): the contract is that auto mode changes NOTHING about the // signatures — the same corpus/query/budget produces byte-identical with and without // --signatures-only, in BOTH budget regimes. The legend is still real bytes downstream: under an // explicit --token-budget the whole auto surface (legend + root attribute + bodies) must fit the // leftover under the stated ceiling or is turned off entirely (see the auto block below), and // est_tokens always measures the emitted header. // COMPACT: the same exemption, for whichever of the two legends is actually on the header — the // contract "the ranked map is byte-identical with and without the enrichment" has to hold for the // compact shape too, or the round would be changing signatures while claiming to change only bodies. const std::size_t autoLegendBytes = enrichmentLegendBytesEmitted( plan, cfg.legend == "compact" ); // CONFIDENCE: the same exemption a third time, for the same reason as D2's adaptiveNote — the // disclosure's contract is DISCLOSURE ONLY, and charging its bytes made the default-budget compact // bundle drop a tail row (measured on this repo's own src, the "rank symbols by pagerank" // fixture query: one row gone at the trim boundary). DEFAULT REGIME ONLY, and this split is the // point where this exemption differs from its two precedents: an explicit --token-budget is a HARD // The disclosure is exempt from the SIG-TRIM allowance in BOTH regimes. Charging it under an // explicit ceiling (the first cut of this change) shrank the sig section BELOW the default // regime's — an explicit ceiling wider than the default re-trimming sigs is exactly the // inversion the frozen sig share existed to forbid (gate: forbudgetmonotoncheck, which caught it). // The est_tokens<=N promise is kept by the BODY side, not by this trim: committedBytes below // counts the emitted header verbatim, so leftBytes hands the bodies ~240 B less and the bundle // total never crosses the ceiling — charging the sig side as well was double-counting one cost. // est_tokens measures the emitted header in both regimes, so nothing under-reports either way. // P1 (L7): under --legend=compact the header carries the SHORT confidence clause and the compact tail legend // (appendCompactForLegend), so the exemption must subtract what was emitted — the full constants left the // ledger 64 B short and the subtraction below underflowed (UBSan: "297 - 361 cannot be represented"). const bool compactLegendOn = cfg.legend == "compact"; const std::size_t confidenceEmitted = compactLegendOn ? ( confidenceEarlyNoteBytes == 0 ? 0 : kForCompactLegendConfidence.size() + ( forCoverageOn ? kForCompactCoverageClause.size() : 0 ) ) : confidenceEarlyNoteBytes; const std::size_t confidenceExemptBytes = confidenceEarlyAttrsBytes + confidenceEmitted + forAtAttrStr.size(); // == confidenceOwnBytes + at= in the full dialect const std::size_t tailLegendEmitted = compactLegendOn ? kForCompactLegendTail.size() : rw::kForFileTailLegend.size(); // DEEP-TAIL: the tail legend's bytes are exempt from the sig-trim charge in BOTH regimes, the // confidence-disclosure precedent verbatim — the tail's contract is that the ranked head is // byte-identical with and without it, and charging the clause here would shrink to pay for // a disclosure. The bytes stay real everywhere downstream: est_tokens measures the emitted header, // and the explicit regime's tail rows are funded from the RESIDUAL (below), never from the sigs. // Row 6 (2026-09-12): the sc=/route= reading (graphlegend.h kForIdRouteLegend) joins the exemption on the // same contract — disclosure only; charging it shrank the explicit-ceiling sig section below the default's // (forbudgetmonotoncheck #1/#5 caught it, 6507 B against 6141 B at --token-budget=8000). Subtract what was // EMITTED in this dialect, exactly as the confidence and tail clauses above do. // A1′: the compact dialect emits ONLY the route clause here (sc= rides its always-on row clause), and only when // the root carries route= — subtract exactly what appendCompactForLegend appended. The DEFAULT dialect emits the // sc= rule always and the same route clause under the same present-only condition (forRouteAttrPresent). // …and the SAME decision the append made (rw::forIdRouteLegendParts), so the ledger cannot subtract a // clause the header never wrote. The compact dialect emits only the route reading here — its sc= reading // rides the always-on rows clause — so it exempts only that part. const rw::ForIdRouteLegendParts idRouteParts = rw::forIdRouteLegendParts( headerParts.idRouteLegend, headerParts.scPresent, !routeNoteRaw.empty() ); const std::size_t idRouteLegendEmitted = compactLegendOn ? idRouteParts.route.size() : idRouteParts.bytes(); // e= (forsigspancheck): its legend clause is a disclosure on the same contract — exempt, so the ranked rows a ceiling // admits are the ones it admitted without it (owner ruling: e= is exempt from the row budget). const std::size_t endLineLegendEmitted = !headerParts.endLinePresent ? 0u : ( compactLegendOn ? rw::kForCompactEndLineLegend.size() : rw::kForEndLineLegend.size() ); // owner-hop: the qword clause — and the via="name" clause when only the owner rows switched it on — are disclosures // on the same contract: exempt, so is byte-identical with and without the owner rows (the rows ride on top of // the hop budget; their reading never costs a signature row). Gate: ownerhopcheck (S). const std::size_t ownerLegendEmitted = ownerLegendExemptBytes( headerParts, compactLegendOn ); // for-body-slot: the slot clause on the same contract — the body rides on top of the complete answer, so its reading never // costs a signature row (forbodyslotcheck (D4): byte-identical with RIPWIRE_NO_BODY_SLOT=1) const std::size_t bodySlotLegendEmitted = bodySlotLegend( headerParts, compactLegendOn ).size(); // body-fold: the fold clause on the same contract — the fold rides on top of the complete answer ( byte-identical with // RIPWIRE_NO_BODY_FOLD=1 but for its next= attribute pair), so its reading never costs a signature row const std::size_t bodyFoldLegendEmitted = bodyFoldLegend( headerParts, compactLegendOn ).size(); // mf1-nx: the caller hop's clause on the same contract — the hop rides on top of the complete answer (, the tail and // the bodies byte-identical with RIPWIRE_NO_CALLER_HOP=1), so its reading never costs a signature row const std::size_t callerHopLegendEmitted = callerHopClause( headerParts, compactLegendOn ).size() + presentOnlyClause( headerParts.epPresent, compactLegendOn, rw::kForCompactEpManifestLegend, rw::kForEpManifestLegend ).size() // ep-manifest: same contract + presentOnlyClause( headerParts.dispatchHopPresent, compactLegendOn, rw::kForCompactDispatchHopLegend, rw::kForDispatchHopLegend ).size() // mf1-nx wave 4: same contract + presentOnlyClause( headerParts.listedPresent, compactLegendOn, rw::kForCompactListedInLegend, rw::kForListedInLegend ).size(); // manifest-noncode: same contract // attrib-multidef Q1: the prov="split" clause is a disclosure on the same contract — exempt, as e= is, so (rows and // its continuation's budget) is byte-identical with and without it (fordisclosurecheck #2: auto vs --signatures-only) const std::size_t splitLegendEmitted = !headerParts.splitPresent ? 0u : ( compactLegendOn ? rw::kForCompactSplitClause.size() : rw::kForSplitClause.size() ); const std::size_t exemptBytes = adaptiveNote.size() + autoLegendBytes + confidenceExemptBytes + tailLegendEmitted + idRouteLegendEmitted + endLineLegendEmitted + ownerLegendEmitted + bodySlotLegendEmitted + splitLegendEmitted + bodyFoldLegendEmitted + callerHopLegendEmitted; if( exemptBytes > headerStr.size() ) { DISCLOSE( "runForLens: header exemptions exceed the emitted header — the sig ledger would underflow; charging the header whole" ); } std::size_t chargedHeaderBytes = exemptBytes > headerStr.size() ? headerStr.size() : headerStr.size() - exemptBytes; chargedHeaderBytes -= std::min( chargedHeaderBytes, rw::forZeroNoteBytes( headerStr ) ); // lean-answers: the zero reading never costs a row // L1 fix round (rv-r1-L1 HIGH-1 / MED-5): THE DEFAULT NEVER CHARGES ITS SIGNATURES MORE HEADER THAN --legend=full WOULD. // The compact header now defines task=/next=/pure=/ (kForCompactLegendFacts), and charging those bytes cost the // default a signature row --legend=full carried (compactlegendcheck (P1): 19 vs 20 rows at --token-budget=3000); making // them exempt instead bought the default rows it could not pay for (over its budget where full fit, 72 functions at 850 // tokens: 995 vs 816). So the compact charge is capped at the FULL dialect's charge for the same header, computed by the // same rule: rows(default) ⊇ rows(full) by construction, and a default that pays more header than full never gets more // room for rows than full had. // forbudget-soft: the FULL dialect's charge for this header, in both dialects — a soft target's text tier is the default // target's sig share as THIS number prices it, so the compact and full answers shrink their rows alike (forSigsBudgets) std::size_t fullChargedHeaderBytes = chargedHeaderBytes; if( compactLegendOn ) { ForLensHeaderParts fullParts = headerParts; fullParts.compactLegend = false; const std::string fullHeader = forLensHeaderText( fullParts, /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); const std::size_t fullHeaderBytes = fullHeader.size(); const std::size_t fullExempt = adaptiveNote.size() + enrichmentLegendBytesEmitted( plan, false ) + confidenceEarlyAttrsBytes + confidenceEarlyNoteBytes + forAtAttrStr.size() + rw::kForFileTailLegend.size() + idRouteParts.bytes() + ( headerParts.endLinePresent ? rw::kForEndLineLegend.size() : 0u ) + ownerLegendExemptBytes( headerParts, /*compact=*/false ) + bodySlotLegend( headerParts, /*compact=*/false ).size() + bodyFoldLegend( headerParts, /*compact=*/false ).size() // body-fold + callerHopClause( headerParts, /*compact=*/false ).size() // mf1-nx + presentOnlyClause( headerParts.epPresent, /*compact=*/false, rw::kForCompactEpManifestLegend, rw::kForEpManifestLegend ).size() // ep-manifest + presentOnlyClause( headerParts.dispatchHopPresent, /*compact=*/false, rw::kForCompactDispatchHopLegend, rw::kForDispatchHopLegend ).size() // mf1-nx wave 4 + presentOnlyClause( headerParts.listedPresent, /*compact=*/false, rw::kForCompactListedInLegend, rw::kForListedInLegend ).size() // manifest-noncode + ( headerParts.splitPresent ? rw::kForSplitClause.size() : 0u ); // attrib-multidef Q1 std::size_t fullCharged = fullExempt > fullHeaderBytes ? fullHeaderBytes : fullHeaderBytes - fullExempt; fullCharged -= std::min( fullCharged, rw::forZeroNoteBytes( fullHeader ) ); // lean-answers: exempt, as above chargedHeaderBytes = std::min( chargedHeaderBytes, fullCharged ); fullChargedHeaderBytes = fullCharged; } // lego-charge: / cost what they are SERVED as — a collapsing section its stub, not its full render // (rw::forSectionsSigChargePlan states both rules and why --sections= keeps the default's charge without a ceiling). // A section the degrade path will stream unmeasured has no bytes here (legoStr/composeStr empty): blockCharge discloses it. // forbudget-soft: the DEFAULT charge in both regimes — a soft target charges the rows alone, and the default // target's share it tiers its text by is the default answer's (forSigsBudgets), sections priced as the default prices them. const bool forSoftTarget = cfg.tokenBudget > 0; // an explicit --token-budget: a SOFT target over the rows const rw::ForStubbableSection legoChargeIn{ .hasContent = !legoStr.empty(), .preCapTotal = legoPreCapCount, .rendered = legoPreRendered, .renderedBytes = legoStr.size() }; const rw::ForStubbableSection composeChargeIn{ .hasContent = !composeStr.empty(), .preCapTotal = composePreCapCount, .rendered = composePreRendered, .renderedBytes = composeStr.size() }; const rw::ForSectionStubPlan sigChargePlan = rw::forSectionsSigChargePlan( cfg.sections, /*explicitCeiling=*/false, cfg.forTask, legoChargeIn, composeChargeIn ); const std::size_t sectionsChargedBytes = sigChargePlan.servedBytes( legoChargeIn, composeChargeIn ) + routeStr.size() + 6; // + "" const std::size_t fixedBytes = chargedHeaderBytes + sectionsChargedBytes; // §F1's graph block, rendered HERE (before ) rather than beside the bodies: it reads only lensRank, so the // bytes are the same wherever it renders. Emission order is unchanged (detail, then graph). rw::ChargedSection graphSection, detailSection; if( cfg.withGraph ) { graphSection = rw::chargeSection( [ & ]( std::FILE* f ) { packGraphBlock( f, ing, lensRank, g.outOff, g.outTargets ); }, rw::kBytesPerTokenDefault ); if( !graphSection.isRendered ) { // the block streams uncharged below: the lens's own contract omits an est_tokens that left it out DISCLOSE( blockCharge, ForLensBlockCharge::DisclosureWhy::SiblingBlockUnmeasured, "runForLens: the graph block streams uncharged — est_tokens omitted" ); } } const std::vector detailIds = cfg.detail > 0 ? forDetailIds( ing, lensRank, cfg.detail, forTopN ) : std::vector{}; // (score desc, id asc) — same order as the sigs // the first --detail body's floor under a soft target (head-cut with next= when it is over a quarter of the target): the // target fills the rows first, and the floor keeps the one body the caller asked for from being cut to nothing const std::size_t detailFloorBudget = forSoftTarget ? forDetailFirstBodyFloor( ing, detailIds, bundleBudget ) : 0u; // THE TWO BUDGETS (forbudget-soft; serialize.h kForPayloadBudgetBytes / kForRunawayGuardBytes): the target the // text-shrink steps aim at, and where the row-drop step aims — the runaway guard (default), or the soft target's rows. // round C (reserve first): a soft target's cut is priced with the full dialect's header and every fixed section — the // graph block included, which the retired ceiling reserved out of the sig claim — so both dialects cut alike const std::size_t cutHeaderBytes = fullChargedHeaderBytes - std::min( fullChargedHeaderBytes, forBodyCeilingDisclosureBytes ); const ForSigsBudgets sigsBudgets = forSigsBudgets( forSoftTarget ? bundleBudget : 0u, fixedBytes, cutHeaderBytes + sectionsChargedBytes + graphSection.xml.size(), /*tierAtDefault=*/autoBundleMode && cfg.packTopN == 0 ); const std::size_t sigsBudget = sigsBudgets.target; // The two attributes SPLICED into the header AFTER the ceiling ladder has chosen a rung — est_tokens // (" est_tokens=\"NNNNNNNN\"", bounded well under 24 B: 8 digits covers ~100M tokens) and, when the // query scored weak, weak="1" (exactly 9 B). Neither exists yet at ladder time, so the ladder cannot // measure them and reserves them instead; both are EXACT-counted into est_tokens itself further down. // Seam-verifier LOW (2026-07-29) found the est_tokens half unpriced (a 3-budget-point window landed // ~15.5% past the bare ceiling with no over_ceiling label); CA4 verifier L2 found the weak="1" half the // same way, 9 bytes spliced in after the number that is supposed to describe them. constexpr std::size_t kEstTokensAttrReserve = 24; constexpr std::size_t kWeakAttrBytes = 9; // exactly ` weak="1"` // N1 (capture-audit verify-wave1 2026-09-04): est_tokens= moved from the header COMMENT onto the // root (a comment-stripping parser read no price at all — the flagship budgeted verb was the one M11 // outlier), so the legend now needs a clause defining it; the clause is fixed text, reserved here and // exact-counted below. over_ceiling="1" and ITS clause are not reserved here: F2 made them reachable from // every rung, so their bytes are not a constant — the ladder prices them per shape (PR #135, // finishForLensHeader). Reserved on the last rung only, they let a bundle ship 70 B past its allowance. // L1 fix round (rv-r1-L1 HIGH-1): the compact dialect never defined the ceilings the root names — budget_tokens= / // max_tokens= rode every budgeted default answer undefined (the full dialect's confidence clause carries them). The // compact clause names the ones THIS root carries; the full dialect's bytes are unchanged (compactlegendcheck A-PIN). const bool compactEstLegend = cfg.legend == "compact"; const std::string_view kForEstTokensLegend = !compactEstLegend ? std::string_view( " est_tokens= prices this bundle in tokens" ) : forGateBudget && forBodyCeiling ? std::string_view( " est_tokens= prices this bundle in tokens; budget_tokens=/max_tokens= the soft row target (rows stop once it is reached)/body ceiling asked" ) : forGateBudget ? std::string_view( " est_tokens= prices this bundle in tokens; budget_tokens= the soft row target asked (rows stop once it is reached)" ) : forBodyCeiling ? std::string_view( " est_tokens= prices this bundle in tokens; max_tokens= the body ceiling asked" ) : std::string_view( " est_tokens= prices this bundle in tokens" ); // F2: spliced ONLY onto a document that actually carries over_ceiling="1" (the attribute is now // reachable from every rung, not just the ladder's last), so a bundle inside its budget keeps every // byte it had. Defines the attribute in the same unit the root prints both numbers in. // R1: the wording lives in serialize.h beside pricedRootAttr now — the MCP `for` twin needs the SAME // sentence and could not reach a constant local to this function. Bound to a local name unchanged so // the byte arithmetic in this block still reads against one identifier. // #61: which sentence depends on which ceilings this root NAMES (forGateBudget / forBodyCeiling, both // settled far above at the H9 splice), so the binding is const rather than constexpr. Keyed on the // attributes present, not on which ceiling was exceeded — see overCeilingLegendFor. A budget-only run // still selects kOverCeilingLegend, so nothing about --for --token-budget moves by a byte. // forbudget-soft: budget_tokens= is a soft target, never a ceiling this root is "over" — only max_tokens= is const std::string_view kForOverCeilingLegend = rw::overCeilingLegendFor( /*namesBudgetTokens=*/false, forBodyCeiling ); const std::size_t headerSpliceReserve = kEstTokensAttrReserve + kForEstTokensLegend.size() + ( forWeak ? kWeakAttrBytes : 0u ); // D10: --token-budget SHAPES this bundle (exit-0 trim) rather than gating it (exit-3 like the // default map/--query) — but the shaped result must still be checkable against the budget it shaped // against. is now buffered too (same open_memstream pattern as lego/compose/routes above) so the // TRUE delivered byte count is known before the header is finalized; est_tokens="N" uses the same // mid-band content rate estimateTokens() uses for the default map (kBytesPerTokenDefault), not the // conservative kMinBytesPerToken the budget CEILING is sized with — this reports what was actually // produced, not the worst-case bound. open_memstream failure degrades to direct emission (no est_tokens // attribute — same as before this change; never a fabricated number). // A2 (survey card, 2026-09-03): how many rank>0 candidates the H1 ladder just below cut — set ONLY on // this (memstream-buffered) render, never on the direct-emission degrade path further down, because // headerStr is already flushed to stdout by the time that path runs and cannot be edited retroactively // (the same reason est_tokens is "omitted", not "wrong", on that path — see its DISCLOSE). std::size_t forDroppedPositive = 0; // knob-honesty-068: a capped names the call that serves it uncut (serialize.h SigsCutContinuation). forbudget-soft: // it is EXEMPT in every regime now — the default has no byte promise (a target and a runaway guard), and an explicit // budget is a soft target with none either — so no row is ever dropped to pay for the handle and the lens renders ONCE // (the C3 unpaid/paid/overrun re-renders are gone with the hard ceiling they priced against). const std::string forRankFlags = forRankShapingFlags( cfg ); const rw::SigsCutContinuation forSigsNext{ cfg.forTask, cfg.packTopN, forRankFlags, fixedBytes, /*json=*/false, /*pasteHandle=*/true }; bool forSigsCapped = false; // did the H1 ladder trim ? — decides the budget_bytes= legend clause below rw::SigsCutReport forSigsCut; // cut-fix lane A: the tag's shown/total/docs_dropped — its clauses below forSigsCut.continuationRequest = &forSigsNext; // knob-honesty-068: IN — the capped block's continuation forSigsCut.rowDropBudgetBytes = sigsBudgets.rowDrop; // forbudget-soft: IN — where step F aims (guard / soft target) forSigsCut.pastTargetDisclosed = !forSoftTarget; // …and the default's rows past its target are disclosed // round C: the render is named so a soft target can narrow its rows past the cut once the sections are served (below); // the redaction tally around it is snapshotted so a second render replaces this one's count instead of adding to it const std::optional redactBeforeSigs = redactPtr != nullptr ? std::optional( *redactPtr ) : std::nullopt; const auto renderSigs = [ & ]( std::FILE* sm ) { packSignatures( sm, ing, lensRank, forTopN, cfg.packBudgetBytes, true, fanInPtr, impurePtr, redactPtr, &forChurn, &forClone, testedPtr, ampPtr, // Q3: churn/clone/tested/amp folded onto the blocks /*rankAdaptivePayload=*/true, // B0.3: tail entries excerpt-trimmed by global rank (serialize.h kForDoc*) sigsBudget, // H1: global payload budget (trim ladder; payload="capped" marker) notesPtr, // L3: field-notes surfacing (inert when null) flRootArg, // R-E: root-relative p= rw::forLensRules( forDocsAfterCode, forEndLines, forHeadRR ), // LB-A floor, e= (endLinesFitCeiling), docs reorder, head RR &forDroppedPositive, // A2: exact count, see droppedPositiveCount &shownSigIds, // lane 2: the rows actually emitted — the tail's rank rows exclude THESE files &forSigsCapped, // did the ladder fire? — the budget_bytes= clause rides only then forTopRowNext, // L-W: the widening page on a thin answer, else the body &forSigsCut, // cut-fix lane A: which cut readings the tag owes; // knob-honesty-068: carries forSigsNext IN SigRowSpelling{ .elideZeroMetrics = true } ); // lean-answers lane: zero cx=/ccx=/in= omitted (legend: absent = 0) }; sigsPreRendered = preRender( renderSigs, sigsStr ); const std::optional redactAfterSigs = redactPtr != nullptr ? std::optional( *redactPtr ) : std::nullopt; if( !sigsPreRendered ) { DISCLOSE( blockCharge, ForLensBlockCharge::DisclosureWhy::SigsUnmeasured, "main: open_memstream failed (or its buffer lost a write) for the sigs block — est_tokens omitted from the header" ); // what a render into a failed buffer measured belongs to bytes that will not be printed: dropped, exactly as a // failed open never sets it, and the direct-emission path below renders the block whole forDroppedPositive = 0; forSigsCapped = false; forSigsCut = rw::SigsCutReport {}; shownSigIds.clear(); } // A2: the splice text, built ONCE here (right after forDroppedPositive becomes known) rather than at // the insert site far below — unlike weak=/est_tokens=, whose PRESENCE is decided before packSignatures // ever runs (so their reserve is a worst-case bound, kEstTokensAttrReserve/kWeakAttrBytes above), this // one is decided BY packSignatures' own return, so its reserve can be the EXACT string that will be // spliced — no bound needed, and no second snprintf later. Every downstream section (bodies, // enrichment, tail, the explicit-ceiling ladder) must see this reserved, or a query that trips the // attribute can push the bundle past its stated --token-budget by the note's own width — measured: // w3fixbudgetcheck's ceiling biconditional and fornotesbudgetcheck's 1500-token fixture both went red // with an early, verbose spelling (a bracketed English explanation, ~120 B) even WITH this reserve // in place — the reserve keeps the document inside the conservative BYTE ceiling, but est_tokens is a // separate, denser measurement these fixtures pin with only ~30 tokens of headroom, and a real 50-token // addition genuinely does not fit either fixture's calibration. Bare spelling instead (~24 B, matching // weak="1"'s own economy) rather than re-anchoring two unrelated fixtures' calibration for one flag: // legendcoveragecheck's ATTR scanner matches literal `` shapes and never sees text // embedded in a comment either way (proven: the gate is green with or without a bracket note here), // so the bracket bought no legend coverage — only bytes. The full explanation lives in the round's // registration (docs/EVALS.md, "A2 round") and the bare form is self-explanatory enough on its own. std::string droppedPositiveNote; if( forDroppedPositive > 0 ) { char nb[ 40 ]; rw::formatTo( nb, sizeof( nb ), " dropped_positive=\"{}\"", forDroppedPositive ); droppedPositiveNote = nb; } // ── budget_bytes= — THE CEILING A DEFAULT RUN NEVER NAMED (METHODOLOGY §9 #6) ────────────────── // H9 above put budget_tokens= on the root, but ONLY when the caller passed --token-budget. A run // without that flag is still budgeted — kForPayloadBudgetBytes is enforced on EVERY --for — so the // default bundle disclosed THAT the ladder had cut it () while the // number that did the cutting appeared nowhere, and a reader could not tell the built-in ceiling // from a caller's own. §9 #6: "a ceiling attribute names the ceiling actually applied". Compare // --pack-task, whose default lands on its root as budget_tokens="6000"; same class of ceiling, two // different honesty outcomes until now. // // THE UNIT IS BYTES, and that is the whole reason the attribute is not simply budget_tokens=. What // the default applies is a BYTE constant: bundleBudget is kForPayloadBudgetBytes verbatim here, and // the ladder compares rendered bytes against it. A token spelling would have to divide by a rate, // and the two rates in play disagree on purpose — est_tokens prices at kBytesPerTokenDefault (2.50) // while a ceiling is SIZED at the conservative kMinBytesPerToken — so any token number printed here // would be one the tool never applied, which is the exact failure §9 #6 names. In the EXPLICIT // regime the caller's own number is the honest one and budget_tokens= already carries it, so this // attribute is default-regime-only: the two never both ride, and never neither. // // SCOPE, stated in the clause because the root also carries est_tokens: this ceiling bounds the // RANKED PAYLOAD (sigs + lego + compose + routes + the charged header). The auto bodies ride // kForAutoBodyBudgetBytes ON TOP of it, so a bundle whose est_tokens prices out above this number // is not over any ceiling — which is also why it is not wired into over_ceiling, whose rule is // stated over the TOKEN ceilings the root names. // // COST: zero unless the ceiling actually bit. Both the attribute and the clause defining it ride on // forSigsCapped — the ladder's own verdict, returned by packSignatures above — which is the // pr_converged shape (src/prconverge.h) and the same conditional splice kForOverCeilingLegend and // kForEstTokensLegend use, for the measured reason recorded there: fornotesbudgetcheck's rungs are // tight enough that an unconditional ~130 B of disclosure re-anchors two unrelated fixtures. // Spliced AFTER the sigs render (with dropped_positive= / autoAttr below), never before it, so the // ladder's own input is untouched and the block stays byte-identical to the pre-fix render — // forbudgetmonotoncheck pins exactly that identity across ceilings. No "--" in the clause: it rides // inside an XML comment, where a double hyphen is ill-formed (G4). const bool forDefaultCeiling = forSigsCapped && cfg.tokenBudget == 0; // forbudget-soft: the default answer's ONLY row cut — the runaway guard (a soft target's row stop is no "over") const bool forGuardCut = forSigsCut.rowDropCut && !forSoftTarget; const std::string sigsCeilingAttr = forDefaultCeiling ? " budget_bytes=\"" + std::to_string( rw::kForPayloadBudgetBytes ) + "\"" : std::string(); // cut-fix lane A: the tag's two cut readings (docs_dropped=, shrunk-not-dropped) ride the same splice, present // only when the tag carries the case (serialize.h sigsCutLegendNotes), so the reserve below already covers them. // forbudget-soft: on an answer whose ranked head ran PAST the target (past_target=), the number shaped row text and // removed no row — its reading says "target" there; elsewhere the payload fit it and the ceiling reading stays true std::string sigsCeilingNote = ( forDefaultCeiling ? std::string( forSigsCut.pastTarget > 0 ? rw::kForBudgetBytesTargetNote : rw::kForBudgetBytesNote ) : std::string() ) + rw::sigsCutReportLegend( forSigsCut ) // + the continuation's and the docs reorder's readings (docs_after_code=) + ( forGuardCut ? rw::forRunawayGuardNote() : std::string() ); // forbudget-soft: the runaway guard's reading, where it cut // ── the INDEXING-cap disclosure (mention.h CapDisclosure), at the same splice point and for the // same reason: a --for header is charged against the payload ceiling, so a disclosure folded into // the notes above is paid for in ranked rows. Measured on sixteen real invocations, that cost three // bundles a row; riding this post-render splice costs bytes and never costs evidence. // COST: zero unless a cap actually bit. const std::string capAttrsStr = lr.capAttrs; const std::string capNoteStr = lr.capNote; // L3 follow-up (CodeRabbit 4053600616): known this early (d.notesDegraded needs no render), folded into // the SAME reserve as the splices above so the ladder's remaining-budget arithmetic sees it too. const std::string notesDegradedAttrStr = d.notesDegraded ? std::string( rw::notes::kNotesDegradedAttr ) : std::string(); const std::string notesDegradedNoteStr = d.notesDegraded ? ( " [" + std::string( rw::notes::kNotesDegradedReading ) + "]" ) : std::string(); const std::size_t droppedPositiveSpliceReserve = droppedPositiveNote.size() + sigsCeilingNote.size() + sigsCeilingAttr.size() + capAttrsStr.size() + capNoteStr.size() + notesDegradedAttrStr.size() + notesDegradedNoteStr.size(); // §P3 × §P4: the budget trim above can drop files the lego scope still references — narrow the lego // block to the RENDERED sigs' files and re-render (a byte-subset of what the budget already charged // for, so the bundle only shrinks; before est_tokens so the header reports the delivered size). if( sigsPreRendered && legoPreRendered && !legoStr.empty() && narrowLegoToRenderedSigs( ing, legoScoped, rw::sigsRowsAtTarget( sigsStr, forSigsCut ), // forbudget-soft: scoped at the target flRootArg.empty() ? std::string_view() : rw::sarif::rootPrefixOf( flRootArg ) ) ) { legoStr = captureXml( [ & ]( std::FILE* f ) { packLego( f, ing, legoScoped, lensRank, 12, redactPtr, impurePtr, kNoNode, /*withPaths=*/true, flRootArg, rw::LegoTargetedArgs().withDecidedOutside( g.baseOutsideClauses ), &legoPreCapCount, &g.implementors ); } ); } // L2 (round-1 lever B1, §9.3 disclosed cut): collapse / to a counted stub BY DEFAULT — // total= is that section's own pre-cap row count, shown="0" discloses that nothing was rendered here, // and next= is the ONE restoring spelling (E41 rule b: nextverb.h's capped composer) that returns // BOTH sections byte-identical to this un-stubbed render, in ONE call. --sections=lego,compose // (cli.h validateSectionsModifier) opts a caller back into the pre-stub render of either or both. No // stub for a section that would have been EMPTY. // // FAULT-INJECTION FIX (independent review, 2026-09-19): total= — and therefore the stub-or-full // decision itself — must be reachable WITHOUT the buffered render legoStr/composeStr came from, // because that render can fail (open_memstream degrades, legoPreRendered/composePreRendered==false) // and the section is then emitted directly to stdout further down (the ORIGINAL degrade path, unaware // of --sections=). legoPreCapRowCount/composePreCapRowCount (serialize.h) compute the SAME count with // no FILE* and no buffering — provably identical to packLego/packCompose's own tally (see their own // comments) — so both paths read the identical number. They no longer share the DECISION: the degrade // path has no measured bytes, so priceSectionStub never collapses there and the section streams whole // (CodeRabbit 4054594306 — a stub is taken only when it is proven cheaper). legoScoped/g.composeEdges/lensSurfaceIds are read here in // whichever state they are already in (post-narrow on the happy path, since narrowing already ran // above; un-narrowed on the degrade path, since narrowing never runs there either) — the same state // each path's own render (buffered or direct-to-stdout) would use. const std::size_t legoStubTotal = legoPreRendered ? legoPreCapCount : rw::legoPreCapRowCount( ing, legoScoped ); const std::size_t composeStubTotal = composePreRendered ? composePreCapCount : rw::composePreCapRowCount( ing, g.composeEdges, lensSurfaceIds ); // R2-L2' (round-2, priced re-registration of L2/B1, rv-prereg2 Amendment 1 R4): a CANDIDATE (has content, not opted // back in by --sections=) collapses ONLY WHEN CHEAPER — rw::planForSectionStubs / priceSectionStub (serialize.h) hold // the one rule both surfaces share, its ONE restoring invocation (used at every site below: the buffered substitution // and the degrade-path fallback), and the DEGRADE path it documents: no rendered bytes to gate on there, so the // section never collapses and is streamed whole below. // round C (reserve first): a soft target serves the sections whole when they fit what the rows at its cut left const std::size_t sigsAtCutBytes = sigsStr.size() - std::min( sigsStr.size(), forSigsCut.pastTargetBytes ); const std::size_t sectionsShare = bundleBudget - std::min( bundleBudget, forHdrXml.size() + sigsAtCutBytes + routeStr.size() + graphSection.xml.size() ); const rw::ForSectionStubPlan stubPlan = rw::planForSectionStubs( forSoftTarget && legoPreRendered && composePreRendered ? rw::forSoftSectionsOptIn( cfg.sections, legoStr.size(), composeStr.size(), sectionsShare ) : std::string_view( cfg.sections ), cfg.forTask, rw::ForStubbableSection{ .hasContent = legoPreRendered ? !legoStr.empty() : legoStubTotal > 0, .preCapTotal = legoStubTotal, .rendered = legoPreRendered, .renderedBytes = legoStr.size() }, rw::ForStubbableSection{ .hasContent = composePreRendered ? !composeStr.empty() : composeStubTotal > 0, .preCapTotal = composeStubTotal, .rendered = composePreRendered, .renderedBytes = composeStr.size() } ); const std::string& sectionsNextInvocation = stubPlan.nextInvocation; const rw::SectionStubPricing& legoPricing = stubPlan.lego; const rw::SectionStubPricing& composePricing = stubPlan.compose; const bool legoWillStub = stubPlan.legoWillStub; const bool composeWillStub = stubPlan.composeWillStub; // postcondition of the rule itself: whichever section actually collapses is, by construction, smaller // than what it replaced (the WHOLE point of pricing it) — never a stub that grew the answer. ENSURES( !( legoPreRendered && legoWillStub ) || legoPricing.stubXml.size() < legoStr.size(), "R2-L2': a lego stub collapsed without being smaller than the section it replaced" ); ENSURES( !( composePreRendered && composeWillStub ) || composePricing.stubXml.size() < composeStr.size(), "R2-L2': a compose stub collapsed without being smaller than the section it replaced" ); // lego-charge: the budget priced a collapsing lego at its PRE-narrow stub; the narrow only empties implementor // lists, so the stub served now is never longer than the one charged (total= can only shrink). ENSURES( !( legoWillStub && sigChargePlan.legoWillStub ) || legoPricing.stubXml.size() <= sigChargePlan.lego.stubXml.size(), "lego-charge: the served lego stub is longer than the stub the budget was charged for" ); std::string sectionsStubNote; // present-only legend clause (kForSectionStubLegend), spliced below if( legoWillStub || composeWillStub ) { sectionsStubNote = rw::kForSectionStubLegend; } // count-floor: the lego COUNT clause rides only on a document whose served carries its attributes (a short // list or a floor) — the same post-render, present-only splice, so an answer without them keeps every byte. if( legoPreRendered && !legoWillStub && legoStr.find( rw::kLegoCountAttrPrefix ) != std::string::npos ) { sectionsStubNote += rw::kForLegoCountLegend; } if( legoPreRendered && legoWillStub ) { legoStr = legoPricing.stubXml; } if( composePreRendered && composeWillStub ) { composeStr = composePricing.stubXml; } const std::size_t sectionsStubSpliceReserve = sectionsStubNote.size(); // ── §F1 (CA4 wave-1 verifier): the lens's LAST TWO payload sections, rendered and CHARGED here ────── // §H7 gave the default map the structural property "a section cannot be APPENDED without being // charged" — and this lens kept two sections outside it, emitted straight to stdout AFTER est_tokens // was already written: --detail=N bodies and --with-graph's mermaid block. MEASURED at the pause: // `--for --token-budget=2000 --detail=20` streamed 68 035 B against a 4 248 B allowance (16x) with // est_tokens="1674" unmoved and stderr empty, while the self-check that concluded "no bypass found" // was taken on the BARE --for — the one shape where both sections are absent. Both now go through // rw::chargeSection for the reason the map's four do: a section that renders through the funnel is // charged by construction, not by a second piece of code remembering to add it. // // ORDER HERE (not the emission order, which is unchanged: detail, then graph): the graph block has no // budget knob, so its size is a FIXED cost; the bodies are the one section with a byte budget, so they // are the section that absorbs whatever the ceiling has left. Pricing the fixed cost first is what lets // the bodies' budget be exact. // (graphSection is rendered above, before — fix round 4 reserves its bytes out of the sig side.) // both kept alive past the render so the isRendered=false degrade path below re-emits the SAME set at // the SAME budget (the map path's emitSection lambda has the identical contract); detailIds is computed above std::size_t detailBodyBudget = 0; // forbudget-soft: WHAT THE TRAILING SECTIONS ARE FUNDED FROM (bodies, hops, tail), in the default answer's priority // order. DEFAULT: the bundle as it stood at the 7.5 KB target — the rows served past it (past_target=) are not charged // to the sections after them, so no section shrinks because the head was kept whole. SOFT TARGET (round C, reserve // first): the same rule at the soft target's cut — the rows the retired hard ceiling served at this N — so each section // keeps at least the share that ceiling gave it; the header and every disclosure are never charged there. const std::size_t sigsFundedBytes = sigsStr.size() - std::min( sigsStr.size(), forSigsCut.pastTargetBytes ); const std::size_t rowsSpentBytes = forHdrXml.size() + sigsFundedBytes + legoStr.size() + composeStr.size() + routeStr.size() + graphSection.xml.size(); const std::size_t headerSpentBytes = forSoftTarget ? 0u : headerStr.size() + 6 + headerSpliceReserve + droppedPositiveSpliceReserve + sectionsStubSpliceReserve; // --detail=N (lever 3): importance-weighted detail — spend FULL bodies on only // the top-N ranked symbols (the head the rank identifies), leaving the rest as the signatures emitted // above. Measured +63% tokens for the 3 relevant heads vs +355% for all-bodies. N is clamped to the // emitted head (forTopN, already narrowed by --adaptive) so a body never references a symbol outside // the lens. N=0 emits nothing → byte-identical to a run without --detail. if( cfg.detail > 0 ) { // Composes with --max-tokens: when set, it bounds the body byte budget (same conservative rate the // map path uses). §F1: --token-budget SHAPES this lens (D10 — trims to fit, always exit 0), so it // has to bound the bodies as well; before this it bounded ONLY and the bodies rode along on // --pack-budget-bytes, which is how a 2 000-token budget delivered 68 KB. The bodies get whatever // the ceiling has left after the header, , the sibling blocks, the graph block, the closing // tag and the two header attributes spliced in below — and never MORE than the budget they already // had, so a run WITHOUT --token-budget is byte-identical. detailBodyBudget = cfg.maxTokens > 0 ? rw::budgetBytesForTokens( std::size_t( cfg.maxTokens ) ) : cfg.packBudgetBytes; if( forSoftTarget ) { // the soft target's rows left this much; never below the first body's floor (forDetailFirstBodyFloor: head-cut // with next= when oversized), so the one body the caller asked for always ships const std::size_t leftBytes = bundleBudget > rowsSpentBytes ? bundleBudget - rowsSpentBytes : 1; detailBodyBudget = std::min( detailBodyBudget, std::max( leftBytes, detailFloorBudget ) ); } detailSection = rw::chargeSection( [ & ]( std::FILE* f ) { packBodies( f, ing, detailIds, detailBodyBudget, g.outOff, g.outTargets, rw::withAnchorEdge( rw::CalleeEdgeBytes( g.outNameOnly, &g.outProv, nullptr ), &g.outAnchorMiss ), cfg.compress, redactPtr, /*ranges=*/nullptr, notesPtr, /*outEmitted=*/nullptr, /*truncateOversizedFirst=*/true, /*withFileContext=*/false, flRootArg, &lensRank, rw::CalleeLegendsInHead( headerParts.viaPresent, headerParts.splitPresent ) ); }, // L3: --detail bodies surface notes too (part of the --for bundle) rw::kBytesPerTokenBody ); if( !detailSection.isRendered ) { DISCLOSE( blockCharge, ForLensBlockCharge::DisclosureWhy::SiblingBlockUnmeasured, "runForLens: the --detail bodies stream uncharged — est_tokens omitted" ); } } // ── T3: the terminal-by-default auto section (pre-registered: docs/EVALS.md §4) ──────────── // The decision + render live in buildForAutoBodies (above runForLens — this function is already one // of the largest in the file); this site only wires its verdict in: keep the section + attribute, or // rebuild the header WITHOUT the legend when its off-switch fires, so the ladder's later rebuilds // honor the decision too. One switch since forbudget-soft: surfaceOff (the chargeSection degrade), which // clears the root attribute as well; a spent soft target keeps the legend and serves the hops cut marker // (hopsCutMarker), whose clause rides and which clears the readings only hop rows could need. ForAutoBodiesResult enrich; // .attr is spliced onto the root after the ladder; its bytes are priced there if( autoBundleMode && sigsPreRendered ) { // the DEFAULT regime funds the bodies from the bundle at its target (sigsFundedBytes) — the tail rides on top there and // the bodies stay what they were; a soft target funds them from the rows' leftover (headerSpentBytes is 0) enrich = buildForEnrichment( cfg, ing, g, lensSurfaceIds, lensRank, plan, routeAnchorDefs, redactPtr, headerSpentBytes + rowsSpentBytes, bundleBudget, ForSectionPromises{ headerParts.viaPresent, &ownerPlan, headerParts.splitPresent } ); if( enrich.surfaceOff ) { headerParts.autoBundle = headerParts.compactBundle = false; headerParts.viaPresent = false; // FE-B: the section that could carry it is gone (autoBundleMode excludes --detail) headerParts.splitPresent = false; // attrib-multidef Q1: likewise headerParts.ownerPresent = false; // owner-hop: the same section headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } else if( headerParts.compactBundle && rankedHopCallsNextPresent( enrich.section.xml ) ) { headerParts.hopsCallsNextPresent = true; // body-fold rider: the clause rides beside the next= it defines headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } else if( autoBundleMode ) { // The pre-render degraded (disclosed above through blockCharge), so no body is served: the bodies // decision and the ladder both need the measured signatures. The root says so with a THIRD reason beside // budget/no_candidates, defined in the same header — the legend promising bodies used to ship with neither // a bodies section nor any attribute saying why. enrich.attr = headerParts.compactBundle ? " bundle=\"compact\" bodies=\"0\" reason=\"degraded\"" : " bundle=\"auto\" bodies=\"0\" reason=\"degraded\""; headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, kForDegradedBundleNote ); } // ── for-body-slot: THE ONE OWNER BODY, after the compact (forowner.h states the pick; the header carries its clause). // DEFAULT regime: a required section of the complete answer, riding on top — , and are byte-identical // to RIPWIRE_NO_BODY_SLOT=1, est_tokens prices it at the body rate, the runaway guard never reads it. SOFT TARGET (reserve // first): funded after the hops from what the rows AT THE CUT left of T; it rides only when its whole render fits that // share, else it is SKIPPED — shown="0" capped="1" next= the call that serves it, root reason="budget" — and the tail's // share and the rows' refill below both charge what it served, so the rows past the cut give way to it. rw::BodySlotRender slot; if( slotOwner.any() && autoBundleMode && sigsPreRendered && !enrich.surfaceOff ) { const std::size_t slotSpent = rowsSpentBytes + plan.attrReserve + enrich.section.xml.size(); // the rows at the cut + the hops slot = rw::renderForBodySlot( rw::BodySlotInputs{ ing, g.outOff, g.outTargets, g.outNameOnly, lensRank, flRootArg, redactPtr, notesPtr, headerParts.viaPresent, cfg.compress, &g.outProv, headerParts.splitPresent, &g.outAnchorMiss }, slotOwner, forSoftTarget ? std::optional( rw::forSoftSectionsShare( bundleBudget, slotSpent ) ) : std::nullopt ); if( slot.served ) { enrich.attr = " bundle=\"compact\" bodies=\"1\" reason=\"owner\""; // never bodies="1" beside reason="compact-route" } else if( slot.skipped ) { enrich.attr = " bundle=\"compact\" bodies=\"0\" reason=\"budget\""; } else { // the render's buffer failed: no body rides and nothing names one — the clause goes with it (the T3 degrade contract) headerParts.bodySlotPresent = false; headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } else if( headerParts.bodySlotPresent ) { headerParts.bodySlotPresent = false; // the compact section degraded (surfaceOff) or the rows were never measured headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, enrich.surfaceOff ? std::string_view() : kForDegradedBundleNote ); } ASSUME( !slot.served || headerParts.bodySlotPresent, "a served owner body rides with the clause that defines it" ); // ── body-fold: THE TOP RANKED BODIES, after the slot (forfold.h states the walk). The default regime only (foldIds above): // it rides on top of the complete answer — , , and the slot are the RIPWIRE_NO_BODY_FOLD=1 bytes but for // the next= pair spliced below — and est_tokens prices it (bodies at the body rate, the envelope and skips as markup). rw::BodyFoldRender fold; if( headerParts.bodyFoldPresent ) { if( sigsPreRendered && !enrich.surfaceOff && !enrich.hopsCutMarker ) { fold = rw::renderForBodyFold( rw::BodyFoldInputs{ ing, cfg.forTask, slot.served ? slotOwner.id : rw::kNoNode, lensRank, flRootArg, redactPtr, cfg.compress, &g }, foldIds ); if( fold.follows > 0 && !fold.xml.empty() ) { headerParts.foldFollowPresent = true; // WRAPPER-FOLLOW: the clause rides beside the attribute it defines headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } if( fold.xml.empty() ) { // the compact section degraded (surfaceOff) or the rows were never measured: no fold, and its clause goes with it headerParts.bodyFoldPresent = false; headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, enrich.surfaceOff ? std::string_view() : kForDegradedBundleNote ); } } // ── mf1-nx: THE CALLER HOP, the ONE function's callers (forcallerhop.h states the rows). The default regime only // (callerHopIds above): it rides on top of the complete answer — every other byte is the RIPWIRE_NO_CALLER_HOP=1 answer's — // and est_tokens prices it as markup. No hop when the bodies section degraded or the rows were never measured. rw::CallerHopRender callerHop; if( headerParts.callerHopPresent ) { if( sigsPreRendered && !enrich.surfaceOff ) { callerHop = rw::renderForCallerHop( ing, g, callerHopIds, flRootArg ); } if( callerHop.xml.empty() ) { headerParts.callerHopPresent = false; // its clause goes with it headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, enrich.surfaceOff ? std::string_view() : kForDegradedBundleNote ); } } // mf1-nx wave 4: THE DISPATCH HOP rides on top of the complete answer like the caller hop (RIPWIRE_NO_DISPATCH_HOP=1 is the // rest of it byte for byte); none when the bodies section degraded or no production candidate is left to name rw::DispatchHopRender dispatchHop; if( headerParts.dispatchHopPresent ) { if( sigsPreRendered && !enrich.surfaceOff ) { dispatchHop = rw::renderForDispatchHop( ing, g, dispatchSites, rw::BodyFoldInputs{ ing, cfg.forTask, rw::kNoNode, lensRank, flRootArg, redactPtr, cfg.compress } ); } if( dispatchHop.xml.empty() ) { headerParts.dispatchHopPresent = false; // its clause goes with it // the degraded note is true only when the sigs were never measured (CR 4419328428): an empty hop over measured sigs is healthy headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, ( !sigsPreRendered && !enrich.surfaceOff ) ? kForDegradedBundleNote : std::string_view() ); } } // ep-manifest: rides on top of the complete answer like the caller hop; none when the bodies section degraded if( headerParts.epPresent && ( !sigsPreRendered || enrich.surfaceOff ) ) { epManifest.xml.clear(); headerParts.epPresent = false; // its clause goes with it headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, enrich.surfaceOff ? std::string_view() : kForDegradedBundleNote ); } if( headerParts.listedPresent && ( !sigsPreRendered || enrich.surfaceOff ) ) // manifest-noncode: same rule as { listedIn.xml.clear(); headerParts.listedPresent = false; headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, enrich.surfaceOff ? std::string_view() : kForDegradedBundleNote ); } const rw::ChargedSection& autoSection = enrich.section; const std::string& autoAttr = enrich.attr; if( enrich.hopsCutMarker ) { // fix round 4: the hops budget-cut marker's present-only clause says what the empty element is sectionsStubNote += headerParts.compactLegend ? kForHopsBudgetCutLegendCompact : kForHopsBudgetCutLegend; // forbudget-soft: the marker carries no row, so the readings only hop rows could need (the via="name" and the // owner qword clauses) have nothing to define — cleared as the old attribute-only shape cleared them, so a // spent target answers byte-identically with and without the owner plan (ownerhopcheck (B2)); the bundle legend stays. // train 28 (CR pre-pass): attrib-multidef's prov="split" reading the same way — the lane cleared it on the retired // spent-ceiling switch this marker replaced — unless a served body slot's rows lean on the header's definition const bool splitOnlyHops = headerParts.splitPresent && !slot.served; if( headerParts.viaPresent || headerParts.ownerPresent || splitOnlyHops ) { headerParts.viaPresent = false; headerParts.ownerPresent = false; headerParts.splitPresent = headerParts.splitPresent && !splitOnlyHops; headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } // ── DEEP-TAIL d2: render the file-grain tail (FOLD C: page 0 of the file page; serialize.h kForFileTailShownCap / kForFileTailPageRows) ── // Rendered after the bodies decision. Default regime: the full 40 rows ride on top — the sigs and the bodies are // byte-identical to a tail-less bundle by construction (nothing above charges these bytes). A soft target: the tail is // the last trailing section, funded from what the rows AT THE CUT and the sections before it left (reserve first). // its rank rows exclude the shown rows' files — under a soft target the rows AT THE CUT, the ones a refill below keeps const FileTail forFileTailShown = sigsPreRendered ? computeFileTail( ing, lensRank, lr.evidence, forSoftTarget ? rowsAtTargetIds( shownSigIds, lensRank, forSigsCut.pastTarget ) : shownSigIds, flRootArg, cfg.forTask ) : forFileTail; // round C: the --detail bodies are the caller's own request under their own ceiling (--max-tokens / the pack budget), so // they neither fund nor are funded by the reserved sections: the tail's share and the rows' refill both leave them out, // and a body ceiling never moves a row (formaxtokenscheck (E)) const std::size_t tailSpentBytes = forSoftTarget ? rowsSpentBytes + autoSection.xml.size() + slot.xml.size() : rowsSpentBytes + detailSection.xml.size() + autoSection.xml.size() + slot.xml.size(); std::string tailStr = renderForFileTailXml( forFileTailShown, cfg.tokenBudget, bundleBudget, tailSpentBytes ); // ── round C (owner decision 2026-10-14, RESERVE SECTIONS FIRST): the rows past the soft target's cut fill what the // trailing sections left of T. The sections above were funded as if the rows stopped at the cut (sigsFundedBytes), so // each kept at least the share the retired hard ceiling gave it; the rows are now narrowed to T less those sections' // served bytes — never below the cut (a row budget at or under the target IS the cut: serialize.h sigsRowDropEffective). // Re-rendered only when that budget is narrower than the first render's, which changes only rows past the cut: the // sections' scope (sigsRowsAtTarget) and funding read the rows AT the cut. The first render's redaction count is // replaced, never added to; a re-render whose buffer fails keeps the first render and its readings whole. if( forSoftTarget && sigsPreRendered && forSigsCut.pastTarget > 0 ) { // the tail is charged at its RESERVE — its share, capped at the whole render of the head-scoped tail (forFileTail: the // rows outside the 40-row surface, the same at every N) — not at its filled bytes nor at the cut-scoped tail's size: // both step with N (a whole row at a time, as a file moves between the rank rows and the page rows), and // the rows past the cut would then fall as N grows. With this reserve the rows are monotone in N. std::vector tailEsc; const std::size_t tailReserve = std::min( std::max( bundleBudget > tailSpentBytes ? bundleBudget - tailSpentBytes : 0u, rw::kForFileTailShellReserve ), rw::renderFileTailXml( forFileTail, forFileTail.paths.size(), tailEsc ).size() ); const std::size_t sectionsServed = forHdrXml.size() + legoStr.size() + composeStr.size() + routeStr.size() + graphSection.xml.size() + autoSection.xml.size() + slot.xml.size() + tailReserve; // not the --detail bodies (above) const std::size_t rowDropFill = rw::forSoftRowDropFill( bundleBudget, sectionsServed ); if( rowDropFill < sigsBudgets.rowDrop ) { const rw::SigsCutReport firstCut = forSigsCut; const std::size_t firstDropped = forDroppedPositive; const bool firstCapped = forSigsCapped; const std::vector firstShown = shownSigIds; const std::optional redactNow = redactPtr != nullptr ? std::optional( *redactPtr ) : std::nullopt; rw::redactTakeBack( redactPtr, redactBeforeSigs, redactAfterSigs ); // the first render's count comes back out forSigsCut = rw::SigsCutReport {}; forSigsCut.continuationRequest = &forSigsNext; forSigsCut.rowDropBudgetBytes = rowDropFill; forSigsCut.pastTargetDisclosed = false; std::string refill; if( preRender( renderSigs, refill ) ) { sigsStr = std::move( refill ); // the first render's spelling of the same attribute (no second fixed buffer: fixedbufsweep's census) droppedPositiveNote = forDroppedPositive > 0 ? " dropped_positive=\"" + std::to_string( forDroppedPositive ) + "\"" : std::string(); sigsCeilingNote = rw::sigsCutReportLegend( forSigsCut ); // a soft target: no default ceiling, no guard reading // lane 2: a row the refill dropped lands in the tail's rank rows — re-listed against the rows now shown, at // the same share (the share, not the rows, is what the refill priced) tailStr = renderForFileTailXml( computeFileTail( ing, lensRank, lr.evidence, shownSigIds, flRootArg, cfg.forTask ), cfg.tokenBudget, bundleBudget, tailSpentBytes ); } else { forSigsCut = firstCut; forDroppedPositive = firstDropped; forSigsCapped = firstCapped; shownSigIds = firstShown; if( redactNow ) { *redactPtr = *redactNow; } } } } // ep-manifest (CodeRabbit 4381174525): the groups were picked from the ranked head BEFORE rendered, and the // renderer may drop a head row (the budget gate, the trim ladder, a soft target's refill). The manifest is re-picked from // the head rows finally emits, so its legend's "registers a shown row" holds; nothing dropped = no change, and a // manifest left with no group goes with its clause (as the degraded branch above). if( ( headerParts.epPresent || headerParts.listedPresent ) && sigsPreRendered ) { std::vector epEmitted; for( const rw::NodeId id : epHead ) { if( std::ranges::find( shownSigIds, id ) != shownSigIds.end() ) { epEmitted.push_back( id ); } } if( epEmitted.size() != epHead.size() && headerParts.epPresent ) { epManifest = rw::renderForEpManifest( ing, epEmitted ); if( epManifest.xml.empty() ) { headerParts.epPresent = false; // its clause goes with it headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } if( epEmitted.size() != epHead.size() && headerParts.listedPresent ) // manifest-noncode: "lists a shown file" holds the same way { listedIn = rw::renderForListedIn( ing, epEmitted, cfg.forTask ); if( listedIn.xml.empty() ) { headerParts.listedPresent = false; headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } } // body-fold C3: the continuation pair, once the fold rode (forfold.h spliceFoldSigsNext; before the root is priced) if( !fold.xml.empty() ) { rw::spliceFoldSigsNext( sigsStr, sigsCeilingNote, fold.firstSkipNext ); } // The text the answer already serves whole, ONE reading for the three late splices below (member-names, FOR-SLOTS, OWNER-CTOR-HOP): // the fold's bodies and member slices, plus the body slot's owner (the fold lists it too when the fold rode). A body section added // here reaches every splice at once; each splice takes the set by const reference and never writes it. const auto servedAnswerBodies = [ & ]() { rw::MemberNamesServed served{ fold.bodies, fold.sliced }; if( slot.served ) { served.bodies.push_back( slotOwner.id ); } return served; }; // ── member-names (formembers.h): mem= on the shown owner rows, spliced into the rendered rows AFTER they were chosen (a name // never costs a row) and before the root is priced (est_tokens prices the names with ). The fold's posture — the compact // route, XML, the default regime — and the whole answer measured (the splice reads the shown ids against the rendered rows). if( autoBundleMode && plan.compact && !cfg.json && cfg.tokenBudget <= 0 && rw::forMemberNamesOn() && sigsPreRendered && !enrich.surfaceOff && !enrich.hopsCutMarker ) { const rw::MemberNamesServed answerBodies = servedAnswerBodies(); if( rw::spliceMemberNames( sigsStr, ing, shownSigIds, cfg.forTask, answerBodies ) > 0 ) { headerParts.memberNamesPresent = true; // the clause rides beside the attribute it defines headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } // ── FOR-SLOTS (forslots.h): slots= on the shown initializer rows, the same posture and the same "text already served" rule if( autoBundleMode && plan.compact && !cfg.json && cfg.tokenBudget <= 0 && rw::forInitSlotsOn() && sigsPreRendered && !enrich.surfaceOff && !enrich.hopsCutMarker ) { const rw::MemberNamesServed answerBodies = servedAnswerBodies(); if( rw::spliceInitSlots( sigsStr, ing, shownSigIds, answerBodies ) > 0 ) { headerParts.initSlotsPresent = true; // the clause rides beside the attribute it defines headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } // ── SLOT JOIN ROWS (forslots.h, IX1-V2): slot_by= / slot_fields= on the shown function and struct rows, the same posture if( autoBundleMode && plan.compact && !cfg.json && cfg.tokenBudget <= 0 && rw::forSlotJoinRowsOn() && sigsPreRendered && !enrich.surfaceOff && !enrich.hopsCutMarker ) { if( rw::spliceSlotJoinRows( sigsStr, ing, shownSigIds, &headerParts.slotNamePresent ) > 0 ) { headerParts.slotJoinPresent = true; // the clause rides beside the attributes it defines headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } // ── OWNER-CTOR-HOP (forctorhop.h): the r=1 method's owner constructor on its row, the same posture and served-text rule if( autoBundleMode && plan.compact && !cfg.json && cfg.tokenBudget <= 0 && rw::forCtorHopOn() && sigsPreRendered && !enrich.surfaceOff && !enrich.hopsCutMarker ) { const rw::MemberNamesServed answerBodies = servedAnswerBodies(); if( rw::spliceCtorHop( sigsStr, ing, g, shownSigIds, answerBodies ) > 0 ) { headerParts.ctorHopPresent = true; // the clause rides beside the attributes it defines headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } } // ── UPWARD-2HOP (forcallerhop.h forRankedCallerHopTargets): an error-shaped task's ranked caller hop, a late splice on the // rows EMITTED (rankedCallerHop states the posture); its clause rides beside it callerHop = rankedCallerHop( RankedHopPosture{ .bundleCompact = autoBundleMode && plan.compact, .sigsRendered = sigsPreRendered, .surfaceOff = enrich.surfaceOff, .hopsCutMarker = enrich.hopsCutMarker, .namedHop = headerParts.callerHopPresent, .routeTag = lr.routeTag }, cfg, ing, g, shownSigIds, flRootArg, std::move( callerHop ) ); if( !callerHop.xml.empty() && !headerParts.callerHopPresent ) { headerParts.callerHopPresent = true; // the ranked clause rides beside the element it defines headerParts.callerHopRanked = true; headerStr = buildForHeader( /*withRouteAttr=*/true, /*withTaskEcho=*/true, {} ); } // PR #135: the priced root this header will carry — every late splice and the est_tokens fixpoint, in // finishForLensHeader (above runForLens, with each splice's rationale). const ForLensRootFinish rootFinish{ .autoAttr = autoAttr, .sigsCeilingAttr = sigsCeilingAttr, .capAttrs = capAttrsStr, .weak = forWeak, .droppedPositiveNote = droppedPositiveNote, .sigsCeilingNote = sigsCeilingNote, .capNote = capNoteStr, .sectionsStubNote = sectionsStubNote, // L2': kForSectionStubLegend, present-only (round-2: only when a section actually collapsed under the priced size gate) .notesDegradedAttr = notesDegradedAttrStr, .notesDegradedNote = notesDegradedNoteStr, .measured = blockCharge.isMeasured, // every pre-rendered block whole: sigs AND its siblings .estTokensLegend = kForEstTokensLegend, .overCeilingLegend = kForOverCeilingLegend, // F2: everything OUTSIDE the header at the markup rate. enrich.markupBytes is the compact section, // folded in here rather than charged separately — one rate, one rounding (ForEnrichmentPlan). .nonHeaderMarkupBytes = forHdrXml.size() + sigsStr.size() + legoStr.size() + composeStr.size() + routeStr.size() + graphSection.xml.size() + enrich.markupBytes + tailStr.size() + slot.markupBytes + fold.markupBytes + callerHop.xml.size() + epManifest.xml.size() + ( dispatchHop.xml.size() - dispatchHop.bodyBytes ) + listedIn.xml.size() + 6, // + "" (deep-tail: the tail at the markup rate) // T3: the body-rate sections; enrich.bodyTokens is zero on the compact route (markup, above); for-body-slot: the // owner body at the body rate (a skipped slot's element is markup, above) .bodyTokens = detailSection.tokens + enrich.bodyTokens + slot.bodyTokens + fold.bodyTokens // body-fold: its bodies + rw::tokensForEmittedBytes( dispatchHop.bodyBytes, rw::kBytesPerTokenBody ), // mf1-nx wave 4: a dispatch body // forbudget-soft: a soft target is never "over" — budget_tokens= names a target, so it is not a ceiling this root // labels against; max_tokens= (the detail bodies' hard ceiling) still is, and the runaway guard's cut is the default's .tokenBudget = 0, .maxTokens = cfg.maxTokens, .bodyCeiling = forBodyCeiling, .guardOver = forGuardCut }; // forbudget-soft: no header ladder. The W3FIX ceiling ladder (task echo / route= / legend clauses dropped "(ceiling)", // over_ceiling's header-floor note) existed to land a HARD ceiling; an explicit --token-budget is a soft target over the // rows now (owner decision 2026-10-14), so the header — every legend clause and the verbatim task echo — always ships. headerStr = finishForLensHeader( std::move( headerStr ), rootFinish ); std::fwrite( headerStr.data(), 1, headerStr.size(), stdout ); // R2-AF (round 2, S4): first rows inside the root, right after the legend — see forHdrRows above. // The SAME string every sum above charged (rendered once, beside forHdrRows) — "" when there is no row. std::fwrite( forHdrXml.data(), 1, forHdrXml.size(), stdout ); if( sigsPreRendered ) { std::fwrite( sigsStr.data(), 1, sigsStr.size(), stdout ); } else { rw::SigsCutReport degradeSigsCut; // knob-honesty-068: carries the continuation request IN, as on the buffered path degradeSigsCut.continuationRequest = &forSigsNext; degradeSigsCut.rowDropBudgetBytes = sigsBudgets.rowDrop; // forbudget-soft: the same two budgets degradeSigsCut.pastTargetDisclosed = !forSoftTarget; packSignatures( stdout, ing, lensRank, forTopN, cfg.packBudgetBytes, true, fanInPtr, impurePtr, redactPtr, &forChurn, &forClone, testedPtr, ampPtr, /*rankAdaptivePayload=*/true, sigsBudget, notesPtr, flRootArg, rw::forLensRules( forDocsAfterCode, forEndLines, forHeadRR ), nullptr, nullptr, nullptr, // the degrade path selects identically forTopRowNext, // L-W: same next= rule on the degrade path °radeSigsCut, // knob-honesty-068: and the same SigRowSpelling{ .elideZeroMetrics = true } ); // lean-answers lane: the same row spelling } if( legoPreRendered ) { std::fwrite( legoStr.data(), 1, legoStr.size(), stdout ); } else if( legoStubTotal > 0 ) { // the degrade path: the buffered render never ran, so the section was never measured and // priceSectionStub refused to collapse it (serialize.h, CodeRabbit 4054594306) — streamed WHOLE, // the same bytes --sections=lego would restore. Same scope+identity (§P3; un-narrowed — the sigs // bytes are unknown here). legoStubTotal == 0 emits nothing, packLego's own no-op. ASSUME( !legoWillStub, "runForLens: an unmeasured lego section was marked for collapse" ); packLego( stdout, ing, legoScoped, lensRank, 12, redactPtr, impurePtr, kNoNode, /*withPaths=*/true, flRootArg, rw::LegoTargetedArgs().withDecidedOutside( g.baseOutsideClauses ), nullptr, &g.implementors ); } if( composePreRendered ) { std::fwrite( composeStr.data(), 1, composeStr.size(), stdout ); } else if( composeStubTotal > 0 ) { // the degrade path, as for lego above: unmeasured, so never collapsed — streamed whole. ASSUME( !composeWillStub, "runForLens: an unmeasured compose section was marked for collapse" ); packCompose( stdout, ing, g.composeEdges, lensSurfaceIds ); } if( routePreRendered ) { std::fwrite( routeStr.data(), 1, routeStr.size(), stdout ); } else if( !g.routeEdges.empty() ) { packRoutes( stdout, ing, g.routeEdges, lensSurfaceIds ); } // DEEP-TAIL d2: the file-grain tail, BEFORE the body sections — a prefix-budgeted consumer meets // the file-grain recall before the big CDATA. Bytes already charged (ladder + est_tokens above). std::fwrite( tailStr.data(), 1, tailStr.size(), stdout ); // ep-manifest: the entry-point groups after the tail, before the caller hop ("" when none) std::fwrite( epManifest.xml.data(), 1, epManifest.xml.size(), stdout ); // manifest-noncode: the manifest lines listing a shown file, beside ("" when none) std::fwrite( listedIn.xml.data(), 1, listedIn.xml.size(), stdout ); // mf1-nx: the caller hop after the tail and before the body CDATA, from the bytes est_tokens priced ("" when none) std::fwrite( callerHop.xml.data(), 1, callerHop.xml.size(), stdout ); // mf1-nx wave 4: the dispatch hop after the caller hop, before the body CDATA ("" when none) std::fwrite( dispatchHop.xml.data(), 1, dispatchHop.xml.size(), stdout ); // §F1: the last two sections, from the bytes already RENDERED and CHARGED above — so what the header // priced and what stdout receives are the same bytes by construction, not by two pieces of code // agreeing. The --detail bodies are ADDED after ///, so the rest of the // bundle keeps signatures-only. A section whose pre-render degraded (isRendered=false) is emitted here // directly at the SAME budget — uncharged for that one run, with an alert, never a fabricated number. if( cfg.detail > 0 ) { rw::emitChargedSection( stdout, detailSection, [ & ]{ packBodies( stdout, ing, detailIds, detailBodyBudget, g.outOff, g.outTargets, rw::withAnchorEdge( rw::CalleeEdgeBytes( g.outNameOnly, &g.outProv, nullptr ), &g.outAnchorMiss ), cfg.compress, redactPtr, /*ranges=*/nullptr, notesPtr, /*outEmitted=*/nullptr, /*truncateOversizedFirst=*/true, /*withFileContext=*/false, flRootArg, &lensRank, rw::CalleeLegendsInHead( headerParts.viaPresent, headerParts.splitPresent ) ); } ); } else if( autoSection.isRendered && !autoSection.xml.empty() ) { // T3: the auto section — same slot as --detail's (after the signature-shaped sections, so // the rest of the bundle keeps its shape). Emitted from the exact bytes est_tokens charged; the // degrade path (isRendered=false) deliberately emits NOTHING — the pre-T3 bundle is the fallback // contract for this optional enrichment, unlike --detail's explicit request (see the block above). std::fwrite( autoSection.xml.data(), 1, autoSection.xml.size(), stdout ); } // for-body-slot: the owner body (or its skip disclosure) after the hops, from the bytes est_tokens priced ("" when none) std::fwrite( slot.xml.data(), 1, slot.xml.size(), stdout ); // body-fold: the top ranked bodies after the slot, from the bytes est_tokens priced ("" when none) std::fwrite( fold.xml.data(), 1, fold.xml.size(), stdout ); // R8: --with-graph — a compact mermaid flowchart of the ranked bundle's anchor neighborhood, // right before . Off by default (G5): omitted, this is a no-op and output is byte-identical. if( cfg.withGraph ) { rw::emitChargedSection( stdout, graphSection, [ & ]{ packGraphBlock( stdout, ing, lensRank, g.outOff, g.outTargets ); } ); } appendSubjectGraphVerbBlock( d, servedAnswerBodies().bodies ); // FOR-ROUTE-DECLINED: "" unless the task is spelled as a graph question that resolves; the bodies this answer carries print no second copy in its path hop bodies // ALIAS-IMPORT (forreexport.h): the `pub use` / `export … from` hops to a definition the task names; "" when none. XML only, like the block above. if( !cfg.json && !cfg.columnar && !cfg.candidates && !d.multiRoot && rw::forReexportOn() ) { const std::string reexports = rw::renderForReexports( ing, cfg.forTask, /*held=*/cfg.tokenBudget > 0 ); // CR pre-pass 42b: held under --token-budget std::fwrite( reexports.data(), 1, reexports.size(), stdout ); } appendLiteralAnchorBlock( d, flRootArg, detailSection.xml + autoSection.xml + slot.xml + fold.xml + callerHop.xml + dispatchHop.xml ); // MF1-LIT: "" unless the task names a literal with a site (dispatchHop: train 42b, its single-candidate ) rw::emitRaw( stdout, "" ); reportRedactions( stderr, redactCounts ); return 0; } return std::nullopt; } // The --for lens: the ranking runs once, the render once (forbudget-soft: no payment-mode re-renders — the lens has no hard // ceiling left to land a charged continuation under). std::optional runForLens( const MainDispatch& d ) { const rw::Config& cfg = d.cfg; if( cfg.forTask.empty() ) { return std::nullopt; } rw::LensRanking lr = computeLensRanking( d, cfg.forTask, forCompactPosture( cfg ), /*fullDistribution=*/!cfg.candidates ); // deep-tail: the bundle serves the file-grain tail; candidates has no tail and keeps the H2 pruning // the at= stamp (gitstamp.h): one read per run — every payment-mode render below shares it, like the ranking const bool forSingleRoot = d.ing.realPaths.empty() && cfg.roots.size() == 1; const std::string forAtStamp = forSingleRoot ? rw::gitstamp::stampAt( d.root ) : std::string(); return runForLensPass( d, std::move( lr ), ForLensPassMode{ .forAtStamp = forAtStamp } ); } std::optional runTargetedViews( const MainDispatch& d ) { using namespace rw; const Config& cfg = d.cfg; const IngestResult& ing = d.ing; const Graph& g = d.g; const std::vector& fanIn = d.fanIn; const QMetrics& qmetrics = d.qmetrics; RedactCounts& redactCounts = d.redactCounts; RedactCounts* redactPtr = d.redactPtr; // R-E (2026-08-17 harvest): same single-root condition every other verb's root= uses (sarif.h) — shared // by --lego and --exemplar, both dispatched from this function. const bool tvSingleRoot = ing.realPaths.empty() && cfg.roots.size() == 1; const std::string_view tvRootArg = tvSingleRoot ? cfg.roots[0] : std::string_view(); const std::string tvRootPrefix = tvSingleRoot ? rw::sarif::rootPrefixOf( cfg.roots[0] ) : std::string(); // --lego=TYPE: the TARGETED Lego view — resolve ONE named interface/base and emit its signature, full // method contract (where sound), and EVERY implementor (own-language only, via the langCompatible guard // in buildGraph) with p= file paths so the agent can open them. Same schema as --for; mirrors the // single-symbol verbs (--around/--expand). file:name disambiguates a same-named type across languages. if( !cfg.legoType.empty() ) { // H1: the out-param is the decl→def widening's residue — same-named definitions a file:name type found and could // not tie to the file it named. implementors= is read off the one node picked here, so unreported, a drop reached // the reader as implementors="0" about a forward declaration. std::size_t legoUnprovenDefs = 0; const NodeId focus = resolveLegoFocus( ing, g.implementors, cfg.legoType, &legoUnprovenDefs ); if( focus == kNoNode ) { // §B4.2: one shared refusal — a non-defining `file:name` says WHICH files define the type and // hands back a runnable retry; a genuinely unknown name still gets the near-miss it always had. rw::emitTo( stderr, "{}\n", selectorNotFoundMessage( ing, "ripwire: --lego type not found: ", cfg.legoType, "--lego=" ).c_str() ); return 1; } const std::vector legoImpure = computeImpure( ing, g ); const std::vector flat( ing.symbols.size(), 0.f ); // single iface → rank is irrelevant (id tie-break) // R-E fix (2026-08-19): the document root DISCLOSES the root its p= are now relative to. The first // R-E landing made packLego's p= root-relative and left the root undisclosed, so a --lego bundle // carried relative paths against a root the reader could not name — the honesty rule this tool sells. // H5: --lego had no legend at all. The #66 clause rides as its own adjacent comment (graphlegend.h // graphUnindexedLegendComment) because kLegoLegend is one closed literal: the attribute below is // conditional on g.unindexedFiles, so its definition has to be too. // H1: the unproven_defs= clause takes the same route for the same reason (graphlegend.h unprovenDefsVerbComment). rw::emitTo( stdout, "{}{}{}{}{}{}", rw::ctxRootOpen( {}, {}, tvRootArg ).c_str(), rw::kLegoLegend, rw::graphUnindexedLegendComment( rw::graphGaugeClauses( g ) ).c_str(), rw::unprovenDefsVerbComment( rw::UnprovenDefsVerb::Lego, legoUnprovenDefs > 0, "", ex( reqNote ).c_str(), kindNote.c_str(), symTag( pick.targetKind ), rw::kExemplarSelectionRule, exemplarVia ? rw::forViaNameClause() : std::string() ); // R-E fix (2026-08-19): root= — same reason as --lego above. p= went root-relative in the first R-E // landing with no attribute naming the root, on the one verb whose whole job is "open this file". const std::string exemplarRootAttr = tvSingleRoot ? ( " root=\"" + ex( tvRootArg ) + "\"" ) : std::string(); rw::emitTo( stdout, "", symTag( pick.targetKind ), pick.candidateCount, ex( wsym.name ).c_str(), ex( tvSingleRoot ? rw::sarif::rootRelativeUri( ing.files[ wsym.fileId ], tvRootPrefix ) : std::string_view( ing.files[ wsym.fileId ] ) ).c_str(), wsym.line, fin( pick.winner ), wsym.ccx, exemplarRootAttr.c_str(), ts( pick.winner ) ? " tested=\"1\"" : "", pick.lowConfidence ? " low_confidence=\"1\"" : "", pick.overCcxBar ? " over_ccx_bar=\"1\"" : "" ); packBodies( stdout, ing, { pick.winner }, cfg.packBudgetBytes, g.outOff, g.outTargets, g.outNameOnly, cfg.compress, redactPtr, /*ranges=*/nullptr, /*noteIndex=*/nullptr, /*outEmitted=*/nullptr, /*truncateOversizedFirst=*/true, /*withFileContext=*/false, tvRootArg, /*calleeRank=*/nullptr, exemplarVia ); rw::emitRaw( stdout, "" ); reportRedactions( stderr, redactCounts ); return 0; } // --recall=TASK: retrieve the most relevant DOCUMENTS (memory notes / design docs) for a task and emit // their FULL bodies, token-budgeted — the memory-as-graph recall verb (pull the relevant few, not the // whole corpus). Same deterministic lexical ranking as --for; relatedness is lexical, not graph (eval). if( !cfg.recall.empty() ) { // §P2 — the two budget flags obey the documented two-personality rule (D10): --max-tokens SHAPES (byte // ceiling at the map family's densest rate × headroom — the old ×4 B/tok overshot ~85×), --token-budget // GATES inside emitRecallBudgeted (exit 3, header line only — never the artifact it rejected). // R-R: the run's own root, derived ONCE and spent twice inside recallFor — the ranker relativizes the // path tokens it scores against it, and buildRecall relativizes the separator line it prints against // it. Two consumers of one fact, because a bundle whose ranking and whose display disagreed about the // spelling of a file is exactly the drift the root-relative emission lane spent a round removing. // Empty for multi-root, where ing.files already hold the labelled root-relative spelling. const std::string_view recallRootArg = ( ing.realPaths.empty() && cfg.roots.size() == 1 ) ? std::string_view( cfg.roots[0] ) : std::string_view(); // Recall is uniquely body-heavy: an unset ceiling used to let a broad docs query stream hundreds of // thousands of tokens. Keep explicit --max-tokens authoritative, but make the common agent path safe. const bool defaultRecallBudget = cfg.maxTokens == 0; const std::size_t recallMaxTokens = defaultRecallBudget ? rw::kDefaultRecallMaxTokens : std::size_t( cfg.maxTokens ); // H9: the TOKEN count is what travels now — recall.h's recallBytesForTokens owns the one conversion, // so the header discloses the ceiling actually applied instead of only the 8000 default. // §B2: --top-k=N now actually SHAPES how many docs recall emits (was accept-and-ignore — --help and // the --limit refusal both already promised this). Default stays 8 when the user never passed the flag. const int recallK = cfg.topKExplicit ? cfg.topK : 8; // recallFor (recall.h) is the ONE rank-then-build call MCP `memory_recall` also makes — the recall // lens's pathFieldDefaultW=1 and its root-prefix derivation live there, so the two front doors cannot // drift apart again (gate: test/recallparitycheck.sh). // F4: --token-budget's GATING ceiling travels to the header too, so both ceilings this run applied are // named where an attribute reader finds them (it used to live only in the withheld prose and stderr). const RecallBundle bundle = recallFor( ing, g.outOff, g.outTargets, cfg.recall, recallK, recallMaxTokens, redactPtr, recallRootArg, // docs only; R-R cfg.tokenBudget > 0 ? std::size_t( cfg.tokenBudget ) : 0u ); const int rc = emitRecallBudgeted( stdout, bundle, cfg.tokenBudget ); reportRedactions( stderr, redactCounts ); return rc; } return std::nullopt; } // L4 — --pack-task="TASK": the budget-shared task bundle. ONE call assembling // the whole 3-5 call orientation dance under ONE deterministic byte budget (default 6K tokens; --token-budget // overrides), in a FIXED section order with a header that reports EVERY truncation (the overbudgetcheck "no // silent caps" precedent). Reuses: the shared lens ranking (computeLensRanking — all --for boosts apply), the // --detail body machinery (packBodies), the --callers in-edge walk, L3 field notes (notesPtr), and the // --affected test-mining (transitiveCallers + isTestPath). Existing verbs stay byte-identical (this path is // only reached when --pack-task is given). Section budgets are consumed in the fixed ALLOCATION order // ranking > bodies > callers > notes > tests: each section gets whatever the higher-priority sections left, so // a tiny budget naturally degrades to ranking-only (each dropped section is named in the header — no silent cap). // // BUDGET CONTRACT (the gate asserts it): the internal target is `budgetTokens × kMinBytesPerToken × // kBudgetHeadroom` bytes; the emitted bundle never exceeds the token CEILING `budgetTokens × kMinBytesPerToken` // except by at most one section's trailing entry (packBodies always emits its first body whole), so a small // documented tolerance covers the overshoot. // The section assembler itself (PackTaskSection / packTaskListSection / the 5-section budget-share machinery / // kPackTask* constants) now lives in packtask.h (L4) as packTaskBundleText() — shared verbatim with the MCP // explore/pack_task verb (mcpverbs.h's packTaskText()). This handler only resolves CLI-specific inputs // (flags, MainDispatch pointers) and hands them to that ONE assembler. // (#362 review, item 2): the budgeted-window branch, extracted — runPackTask's own complexity was // the quality gate's finding (19 -> 28 as the paging round grew the branch inline). One helper, one // job: refuse a partitioned window, serve the candidate page, report the redaction tally. inline std::optional runPackTaskPage( const MainDispatch& d, const rw::LensRanking& lr, const std::string& task, const rw::PackTaskInputs& in ) { using namespace rw; const Config& cfg = d.cfg; const IngestResult& ing = d.ing; if( cfg.partitionCount > 0 ) { rw::emitRaw( stderr, "ripwire: --partition and a --limit/--offset window are mutually exclusive — a partitioned bundle has N+1 slices and no single candidate order to page; drop one\n" ); return 1; } const AdaptiveCut packCut = adaptiveCut( lr.rank, 5, std::size_t( cfg.packTopN > 0 ? cfg.packTopN : kForLensDefaultTopN ), true ); const std::string packAtStamp = in.rootArg.empty() ? std::string() : gitstamp::stampAt( std::string( in.rootArg ) ); const std::string packRankFlags = forPageRankFlagArgv( cfg ); const int packPageRc = emitForCandidatePage( ing, lr.rank, packCut, rw::ForCandidatePageReq{ task, "--pack-task=", packRankFlags, /*pasteHandle=*/true, /*compactLegend=*/cfg.legend != "full", lr.routeNote, in.rootArg, d.redactPtr, packAtStamp, cfg.tokenBudget, cfg.pageLimit, cfg.pageOffset } ); reportRedactions( stderr, d.redactCounts ); // W3-N1: the page redacts — the tally reports return packPageRc; } std::optional runPackTask( const MainDispatch& d ) { using namespace rw; const Config& cfg = d.cfg; const IngestResult& ing = d.ing; const Graph& g = d.g; if( !cfg.packTaskFlag ) { return std::nullopt; } if( cfg.packTask.empty() ) // refuse loudly without a task string (never fall through to the default map) { rw::emitRaw( stderr, "ripwire: --pack-task: a task string is required — e.g. --pack-task=\"add retry to the http client\"\n" ); return 1; } const std::string task( cfg.packTask ); // ── the routed+anchored lens ranking, shared verbatim with --for (all existing boosts apply) ─────────── // B1 (#362 review round 4): a window pages the WHOLE positive-score distribution — a pruned ranking is // exact only for its top-K, so total= and the far pages would depend on a performance optimisation // (measured: 931 vs 1232 candidates on src/). The rule --for's bundle path already follows. The un-paged // bundle keeps the H2 pruning: its consumers read only the head. const bool packWindowPages = candidatePageWindowed( cfg ); LensRanking lr = computeLensRanking( d, task, /*compactCandidate=*/false, /*fullDistribution=*/packWindowPages ); // Q3 per-file churn (mirror --for): only mined on the --for git pass, so here it stays empty/zero when // --for wasn't also given → the churn= attr is simply omitted by packSignatures (nullptr-safe). std::vector forChurn = d.forChurn; if( forChurn.size() != ing.files.size() ) { forChurn.assign( ing.files.size(), 0u ); } PackTaskInputs in; in.budgetTokens = cfg.tokenBudget; // F5: stays std::size_t end-to-end (0 ⇒ the shared default) in.softTarget = cfg.tokenBudget > 0 && cfg.partitionCount == 0; // forbudget-soft: the caller's budget is a soft target in.compactLegend = cfg.legend == "compact" && !cfg.json; // L1 fix round: the ladder prices what the compact layer delivers in.sigLadderBudgetBytes = cfg.packBudgetBytes; in.compress = cfg.compress; in.fanIn = d.fanInPtr; in.impure = d.impurePtr; in.churn = &forChurn; in.tested = d.testedPtr; in.amp = d.ampPtr; in.redact = d.redactPtr; in.notes = d.notesPtr; in.notesDegraded = d.notesDegraded; // L3 follow-up (CodeRabbit 4053600616) // R-E (2026-08-17 harvest): same single-root condition every other verb's root= uses (sarif.h). in.rootArg = ( ing.realPaths.empty() && cfg.roots.size() == 1 ) ? cfg.roots[0] : std::string_view(); // ── THE BUDGETED-BUNDLE CANDIDATE PAGE (paging PoC, issue #294): --pack-task under --token-budget // with an explicit window pages its ranked candidates, mirroring --for (the shared inline). The cliff // statistic is computed here ONCE (the same call --for's disclosure uses); --partition and a window // refuse together — a partitioned bundle has N+1 slices and no single candidate order to page. // (#362 review, item 2): the branch body lives in runPackTaskPage — this stays the one-line gate if( cfg.tokenBudget != 0 && ( cfg.pageLimit > 0 || cfg.pageOffset > 0 ) ) { return runPackTaskPage( d, lr, task, in ); } // --partition=N: the FAN-OUT form. Same lens ranking, same PackTaskInputs, same // assembler; partition.h only decides WHICH slice each of the N+1 bundles is masked to and how the // per-agent budget divides. Handled before the single-bundle emission below because it replaces the // document, not a section of it. cli.h has already bounded N to 2..16 and refused a bare --partition. if( cfg.partitionCount > 0 ) { const std::uint32_t partitionCount = std::uint32_t( cfg.partitionCount ); // --with-graph splices its mermaid block into ONE bundle's ; there are N+1 here and no single // place it belongs. Say so on stderr rather than dropping it silently (it is not worth failing the // whole run over — the bundles themselves are unaffected). if( cfg.withGraph ) { rw::emitRaw( stderr, "ripwire: --with-graph is not applied in --partition mode (N+1 bundles, no single graph) — bundles emitted without it\n" ); } if( cfg.json ) { std::string js; packpartition::packTaskPartitionText( ing, g, task, lr, in, partitionCount, &js ); std::fwrite( js.data(), 1, js.size(), stdout ); } else { const std::string doc = packpartition::packTaskPartitionText( ing, g, task, lr, in, partitionCount ); std::fwrite( doc.data(), 1, doc.size(), stdout ); } reportRedactions( stderr, d.redactCounts ); return 0; } // L2: --json — same section decisions, JSON shape, computed by the SAME packTaskBundleText call the XML // path uses (its optional jsonOut tail), so truncation reporting can never drift between the two shapes. if( cfg.json ) { // §B1.3 (capture-audit-4): --with-graph has no JSON dialect yet (the mermaid block is XML-only, // spliced in only on the plain-text path below) — warn once and continue without it, the same // warn-and-continue shape the --partition branch above already uses, rather than silently dropping // the flag's effect with no tell at all. if( cfg.withGraph ) { rw::emitRaw( stderr, "ripwire: --with-graph is not applied under --json (the mermaid graph block is XML-only for now) — emitted without it\n" ); } std::string js; packTaskBundleText( ing, g, task, lr, in, &js ); std::fwrite( js.data(), 1, js.size(), stdout ); reportRedactions( stderr, d.redactCounts ); return 0; } // R8: --with-graph — a compact mermaid flowchart of the ranked bundle's anchor neighborhood, spliced // in right before the bundle's closing (the extracted packTaskBundleText owns the tag; splitting // here keeps the MCP explore/pack_task path graph-free, which has no --with-graph surface). Off by // default (G5): omitted, this is a no-op and output is byte-identical. // // §F1: RENDERED AND CHARGED FIRST. Spliced in after the assembler had already divided the budget and // priced its ceiling ladder, its ~399 B rode in uncharged — MEASURED `--pack-task --token-budget=800 // --with-graph` = 2 445 B against a 2 171 B allowance, 12.6% over with no over_ceiling, where the bare form // at the same budget was conformant. in.trailingSectionBytes hands the assembler the size BEFORE it spends // the budget (PackTaskInputs documents both places it lands). The degrade path (isRendered=false) splices // nothing and streams the block directly, so the bytes are identical and only the charge is lost. rw::ChargedSection graphSection; if( cfg.withGraph ) { graphSection = rw::chargeSection( [ & ]( std::FILE* f ) { packGraphBlock( f, ing, lr.rank, g.outOff, g.outTargets ); }, rw::kBytesPerTokenDefault ); in.trailingSectionBytes = graphSection.xml.size(); // 0 on the degrade path — the pre-§F1 accounting for that one run in.isTrailingUncharged = !graphSection.isRendered; // that degrade was disclosed by the section's sink; the root says so } std::string bundle = packTaskBundleText( ing, g, task, lr, in ); if( cfg.withGraph && bundle.size() >= 6 && bundle.compare( bundle.size() - 6, 6, "" ) == 0 ) { std::fwrite( bundle.data(), 1, bundle.size() - 6, stdout ); rw::emitChargedSection( stdout, graphSection, [ & ]{ packGraphBlock( stdout, ing, lr.rank, g.outOff, g.outTargets ); } ); rw::emitRaw( stdout, "" ); } else { std::fwrite( bundle.data(), 1, bundle.size(), stdout ); } reportRedactions( stderr, d.redactCounts ); return 0; } } // namespace — verbs_for.h section of main.cpp