You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every item below is a fix that is already understood and mechanically implementable — it is blocked on a number or on a tradeoff nobody has ruled on, not on engineering. What is needed is one sitting with the whole table: write a value next to each row (a byte cap, a deadline, a retention window, a batch size), and rule on the handful of genuine forks (is a cap configurable at all? may a served turn opt out of the host's spend ceiling? which of the two cache-warmth contracts is the real one?). Once the numbers exist, the implementations follow.
Please decide the reporting half at the same time: for every cap that drops, truncates, or evicts, say whether the operator sees it (event, warning, Evicted marker, drop-report row) — a silent drop is the failure mode nobody notices until it has already cost them a session.
Spot-checked against origin/main at 092b2dda (0.5.24): the MCP consts, MAX_TOOL_PAGES, the SSE buffer cap, the bash/exec budget split, the two cache-warmth contracts, download_bytes, and the zai_video_ => arm are all still exactly as described.
Items
MCP budgets are not configurable by any caller.MAX_TOOL_RESULT_BYTES / MAX_TOOLS_PER_SERVER / MAX_TOOL_DESCRIPTION_CHARS / MAX_TOOL_SCHEMA_BYTES are hardcoded pub(crate) consts; making them configurable means plumbing a limit through McpToolSet::connect / connect_with_auth or McpClient::connect / connect_with_auth. Deferred because those are the crate's primary public entry points, used by stella-cli (agent.rs, candidate_ws.rs) — an owner API decision, not a triage fix. stella-mcp/src/client/ingest.rs (the four consts now live here, not in toolset.rs/client.rs), stella-mcp/src/toolset.rs, stella-mcp/src/client.rs, stella-cli/src/agent.rs, stella-cli/src/candidate_ws.rs(audit stella-mcp: nothing bounds what an untrusted MCP server pushes into the model's context #551, deferred by fix(stella-mcp): bound what an untrusted MCP server pushes into context #586)
The MCP budget values are not ratified: 100000 bytes/tool result, 256 tools/server, 2000 description chars, 16384 schema bytes. They shipped as conservative, clearly-documented consts to close the vulnerability, explicitly flagged as "owner-tunable starting points, not ratified values — please push back on any of them." stella-mcp/src/client/ingest.rs (path corrected 2026-07-26 — all four consts moved here: MAX_TOOL_RESULT_BYTES at :51, MAX_TOOLS_PER_SERVER at :63, MAX_TOOL_DESCRIPTION_CHARS at :67, MAX_TOOL_SCHEMA_BYTES at :73) (audit stella-mcp: nothing bounds what an untrusted MCP server pushes into the model's context #551, deferred by fix(stella-mcp): bound what an untrusted MCP server pushes into context #586)
Bound the recall candidate set before MMR — truncate ordered to ~max_frames * 4 (floored at LEXICAL_LIMIT) before the MMR pass and before frame construction, folding the discarded tail into dropped as a FrameCount summary; and cache the decoded vector index in the store keyed by fingerprint (invalidated on store_embedding) instead of re-decoding every stored vector per recall. Deferred because the fix changes the contents of the drop report, which is the crate's observable contract (L-C5) and is covered by property tests — two coupled changes with no agreed drop-report shape. stella-context/src/retrieval.rs:180(audit Batched audit cleanup: area:context — 15 items #558, deferred by chore(stella-context): document the public surface and cache store_kinds #589) — landed in audit: five P0s, two red-main repairs, and one switch for every tool #707: the fused list is cut to 4x max_frames before MMR and before any frame is built, and the tail is summarized into the drop report from the node rows so L-C5's partition still holds. (Witness recall_does_not_scale_quadratically_with_lifetime_memory_size — 7260 cosine calls with the bound removed.)
Chunk the writeback upsert embed call at 64 to match warm_index, so a large delta cannot exceed a batch-limited embedding backend's request/payload limit once a real ONNX/hosted embedder replaces HashEmbedder. Deferred: it changes the embed loop's error/partial-progress semantics on the write path, the current code deliberately unzips to avoid doubling peak memory, and it is unverifiable without a real batch-limited backend. stella-context/src/writeback.rs:401(audit Batched audit cleanup: area:context — 15 items #558, deferred by chore(stella-context): document the public surface and cache store_kinds #589)
Admission control on the serve loop — a max_live_turns cap returning 429 + Retry-After. Deferred: new ServeConfig fields (constructed by tests/http.rs outside the crate) plus a background reaper, and Session's Drop must join or signal its thread — a substantial lifecycle change. stella-serve/src/server.rs:222, stella-serve/tests/http.rs(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 23, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)
Distinguish TooLarge from a clean hangup in read_request and reply 413/400 instead of conflating them; also revisit the 1 MiB body cap. Deferred: it changes read_request's signature and the response behavior of every malformed request — and the 1 MiB cap itself likely needs raising, which is a policy decision. stella-serve/src/http.rs:59(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 20, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)
A deadline for a video job whose provider never reports a terminal status.poll_video maps every unrecognized task_status to MediaJobState::Running, whose is_terminal() is false, so a future CANCELLED/TIMEOUT/EXPIRED makes the job immortal and the agent-driven poll loop burns round trips and tokens forever. Two candidate designs: an age-aware terminal check on MediaJobStatus, or a caller-side deadline bounded by job.submitted_at reporting Failed("provider never reported a terminal status within N minutes"). Deferred because picking the layer and the N is owner judgment, and the failure mode is not reproducible locally. stella-media/src/adapters/zai_video.rs:244(audit Batched audit cleanup: area:media — 10 items #565 item 2, deferred by chore(stella-media): fix the live-smoke gate, preview labels, branding #591)
A max download size for provider-supplied assets.download_bytes buffers an entire provider response into memory with no maximum; READ_TIMEOUT (60s) bounds only silence between reads, so a slow-but-steady body grows unbounded until the CLI is OOM-killed — host-memory DoS from a compromised or misbehaving vendor URL, on the shared image+video call site. Remediation: a max_bytes parameter or per-kind consts (e.g. 64 MiB image / 512 MiB video), a response.chunk() accumulation loop, an up-front Content-Length check, and MediaError::Malformed("<provider> asset exceeded the N MiB download limit") on overflow. Deferred: the numbers are policy the owner sets, and the OOM failure mode is not reproducible in a local test. stella-media/src/http.rs:98(audit Batched audit cleanup: area:media — 10 items #565 item 6, deferred by chore(stella-media): fix the live-smoke gate, preview labels, branding #591) — landed in fix: the nine P0s from the backlog triage #658 (media download caps).
A latency ceiling on ContextRecallPort::recall. There is none anywhere in the crate; tokio::join!(self.triage(...), recall_future) means a wedged embedding call or unresponsive CGP host hangs the whole pipeline before the first stage completes, with no event after Stage { ContextRecall }. Fix: add recall_latency_ceiling: Duration to PipelineConfig, wrap recall_future in tokio::time::timeout, degrade to Recall::default(). Deferred because PipelineConfig is re-exported from lib.rs (public API change) and the default ceiling is an owner judgement call. stella-pipeline/src/pipeline.rs:531(audit Batched audit cleanup: area:pipeline — 15 items #567, deferred by test(pipeline): name the work-item test modules for what they pin #594) — landed in fix: the nine P0s from the backlog triage #658: recall_latency_ceiling on PipelineConfig (which gains a field but is deliberately not made non_exhaustive — all 30 construction sites use ..Default::default()).
Cap the gather_diff subprocess fan-out. One UntrackedNumstat subprocess per changed untracked path, sequentially and uncapped, on every verification observation and after every revision turn — a turn creating many untracked files spawns thousands of git processes inside verify, repaid per revision × per candidate. Fix: cap the per-gather fan-out (~64) or add a batched DiagnosticInvocation::UntrackedNumstatBatch { paths } variant. Deferred: both change observable diff-line accounting, the ~64 is a guessed constant, and the batched variant is a public enum change in ports.rs. stella-pipeline/src/pipeline.rs:2149, stella-pipeline/src/ports.rs(audit Batched audit cleanup: area:pipeline — 15 items #567, deferred by test(pipeline): name the work-item test modules for what they pin #594) — landed in perf/reliability/accuracy: eight audit-backlog fixes, reconciled onto main's new durability contract #703 as bounded concurrency (16 in flight), deliberately not the truncating cap this row proposed: the count feeds the zero-diff guard and the diff-size budget, so dropping the tail would let a large untracked change slip under a budget it should trip.
A recall token budget for the assembled prompt.assemble_user_message concatenates every recalled frame's full content with no budget, no per-frame truncation and no frame-count cap; RecalledFrame::token_cost is summed for the ContextRecall event but never bounds anything, so a mis-tuned recall port silently inflates every subsequent turn (it is in base_messages, so N candidates × every revision pay it) and can push past the context window with no diagnostic. The same unbounded frames flow to plan::build_planner_prompt and witness::witness_prompt. Deferred because it changes prompt bytes — it moves every golden trajectory under stella-pipeline/tests/fixtures/golden/ and needs a deliberate budget default rather than an auditor's guess. stella-pipeline/src/pipeline.rs:2311(audit Batched audit cleanup: area:pipeline — 15 items #567, deferred by test(pipeline): name the work-item test modules for what they pin #594)
Read timeout + connection semaphore on the serve listener (the DoS-shaped filing). No read deadline on either loop, and server.rs:137-145 spawns an unbounded task per accepted connection before auth. The exposure is confirmed, but the prescribed fix is hazardous: a semaphore bounding in-flight connections would be held for the whole lifetime of the long-lived SSE stream, starving the provider-result/tool-result POSTs the same turn must deliver over separate connections — a self-deadlock of the reverse-RPC protocol. It also adds a public ServeConfig field and requires picking timeout/concurrency values. stella-serve/src/http.rs:48, stella-serve/src/server.rs(audit Batched audit cleanup: area:core (2/2) — 31 items #560, deferred by chore(audit): apply the area:core (2/2) batch — 13 of 31 findings #595)
The two cache-warmth contracts disagree at the same instant.CacheWarmth::from_elapsed(300, 300) reports expired at the TTL boundary while is_cache_expired_rewrite(300, w, 300) returns false (strict >), so a scheduler consulting CacheWarmth and a counter consulting is_cache_expired_rewrite classify the same moment differently. Both behaviors are explicitly test-pinned (warmth_counts_down_and_saturates_to_expired, expired_rewrite_needs_both_a_cold_gap_and_an_actual_write); picking one contract is a product decision. stella-model/src/cache_economics.rs:186(audit Batched audit cleanup: area:model — 18 items #566, deferred by fix(stella-model): land the area:model audit batch (#566) #598)
Cap the SSE decoder buffer.SseDecoder.buf grows without bound (no MAX_BUFFERED_EVENT_BYTES) when a provider or broken/hostile proxy streams megabytes without a blank line, and poll's per-event drain(..boundary + delim_len) is quadratic; the fix needs the cap plus a consumed cursor. Deferred as a change to the decoder's error surface, which every adapter maps onto its own ProviderError classification — choosing the cap value and the new terminal error is a design call with workspace-wide reach. stella-model/src/sse.rs:91(audit Batched audit cleanup: area:model — 18 items #566, deferred by fix(stella-model): land the area:model audit batch (#566) #598) — landed in fix: the nine P0s from the backlog triage #658: MAX_BUFFERED_EVENT_BYTES plus the consumed cursor, which also removed a second quadratic the witness found in next_event_boundary (the 50k-event test went 12.03s → 0.05s).
Cross-provider pricing fallback. The follow-on to the Catalog::resolve → resolve_for(provider, &model) scoping change: a model slug that exists in the catalog only under a different provider id resolves to None → cost_usd 0.0, which three new scoping witnesses now pin. Choosing between None/0.0, a cross-provider fallback, or a hard error is a metering-policy decision. stella-model/src/anthropic.rs, stella-model/src/openai.rs, stella-model/src/zai.rs(audit Batched audit cleanup: area:model — 18 items #566, deferred by fix(stella-model): land the area:model audit batch (#566) #598)
Bound references()'s corpus scan. It opens and read_to_strings every file in the index until it accumulates 50 hits, so a rare or zero-match identifier costs a full synchronous read of the entire indexed corpus on the caller's thread — tens of thousands of syscalls, hundreds of MB, per call on a 10k-file workspace. Three mutually-exclusive designs were offered with no bound named for any of them: cap files opened per call, prefilter to files whose symbol rows already mention the name, or back it with an FTS5 index. All three change observable results; picking both the design and the cap is a retrieval-quality decision, and the FTS5 option is a new index, not a cleanup. stella-graph/src/frames.rs:68(audit Batched audit cleanup: area:graph — 15 items #563, deferred by perf(stella-graph): bound the audit's unbounded loops, scans, and walks #600)
Cap SessionModel::files / FileLedger::records — touch_file clones the full diff and pushes unboundedly. Confirmed, but all three sub-changes alter the pure fold the replay-determinism proptest pins, and the eviction bound plus the user-visible Evicted marker are product decisions with no obviously right value. stella-tui/src/model.rs:774(audit Batched audit cleanup: area:tui — 26 items #571 item 10, deferred by chore(stella-tui): clear the area:tui audit batch (9 items) #602)
Harden the clipboard attachment write and add a prune policy. Confirmed. The hardening is a #[cfg(unix)] / #[cfg(not(unix))] split whose non-unix branch could not be compiled locally (shell.rs shows the crate really does maintain a separate Windows path), and the prune policy (age vs count, threshold) is an unspecified decision on a directory other crates read via default_attachments_dir(). stella-tui/src/clipboard.rs:70, stella-tui/src/shell.rs(audit Batched audit cleanup: area:tui — 26 items #571 item 14, deferred by chore(stella-tui): clear the area:tui audit batch (9 items) #602)
Reconcile the bash vs exec output budget, and lower web_fetch's clamp. Either align bash's MAX_OUTPUT_BYTES = 100_000 with the shared exec::MAX_OUTPUT_BYTES = 30_000, or document why a shell needs 3.3x the budget of a build; and lower web_fetch's 400,000-char upper clamp to something a context window can absorb (~120k chars). An explicit either/or on a user-visible output budget: lowering the cap changes what every bash-enabled session sees, and writing the justification for keeping it requires the owners' actual rationale. stella-tools/src/bash.rs:31, stella-tools/src/exec.rs, stella-tools/src/web.rs(audit Batched audit cleanup: area:tools — 26 items #570, deferred by chore(stella-tools): bound tool output, harden sub-resource auth, prune the process table #603)
Why these are together
None of these is an open engineering question — every one has a known fix that was written down and then stopped at the same wall: a constant nobody has authority to invent, or a fork between two defensible behaviors. They span nine crates and eleven audit issues, but they are one unit of work for the person who can rule on them: sit down once with the whole table, write a number in each row, and the code changes become mechanical follow-ups that can be split however is convenient. Deciding them one at a time is what has kept them open, because each in isolation looks too small to justify a policy conversation and too policy-shaped to settle in a bug fix.
Deferred during the #546 audit remediation. Put Closes #<this issue> in the PR description and as a commit trailer when it lands.
What decision this asks for
Every item below is a fix that is already understood and mechanically implementable — it is blocked on a number or on a tradeoff nobody has ruled on, not on engineering. What is needed is one sitting with the whole table: write a value next to each row (a byte cap, a deadline, a retention window, a batch size), and rule on the handful of genuine forks (is a cap configurable at all? may a served turn opt out of the host's spend ceiling? which of the two cache-warmth contracts is the real one?). Once the numbers exist, the implementations follow.
Please decide the reporting half at the same time: for every cap that drops, truncates, or evicts, say whether the operator sees it (event, warning,
Evictedmarker, drop-report row) — a silent drop is the failure mode nobody notices until it has already cost them a session.Spot-checked against
origin/mainat092b2dda(0.5.24): the MCP consts,MAX_TOOL_PAGES, the SSE buffer cap, the bash/exec budget split, the two cache-warmth contracts,download_bytes, and thezai_video_ =>arm are all still exactly as described.Items
MAX_TOOL_RESULT_BYTES/MAX_TOOLS_PER_SERVER/MAX_TOOL_DESCRIPTION_CHARS/MAX_TOOL_SCHEMA_BYTESare hardcodedpub(crate)consts; making them configurable means plumbing a limit throughMcpToolSet::connect/connect_with_authorMcpClient::connect/connect_with_auth. Deferred because those are the crate's primary public entry points, used by stella-cli (agent.rs,candidate_ws.rs) — an owner API decision, not a triage fix.stella-mcp/src/client/ingest.rs(the four consts now live here, not intoolset.rs/client.rs),stella-mcp/src/toolset.rs,stella-mcp/src/client.rs,stella-cli/src/agent.rs,stella-cli/src/candidate_ws.rs(audit stella-mcp: nothing bounds what an untrusted MCP server pushes into the model's context #551, deferred by fix(stella-mcp): bound what an untrusted MCP server pushes into context #586).stella/mcp.tomlkeys instella-mcp/src/config.rs, nostella-cli/src/settings.rssurface, no docs. Deferred as the same public-signature disclaimer in another form, plus it adds a user-facing config schema that needs product sign-off.stella-mcp/src/config.rs,stella-cli/src/settings.rs(audit stella-mcp: nothing bounds what an untrusted MCP server pushes into the model's context #551, deferred by fix(stella-mcp): bound what an untrusted MCP server pushes into context #586)stella-mcp/src/client/ingest.rs(path corrected 2026-07-26 — all four consts moved here:MAX_TOOL_RESULT_BYTESat:51,MAX_TOOLS_PER_SERVERat:63,MAX_TOOL_DESCRIPTION_CHARSat:67,MAX_TOOL_SCHEMA_BYTESat:73) (audit stella-mcp: nothing bounds what an untrusted MCP server pushes into the model's context #551, deferred by fix(stella-mcp): bound what an untrusted MCP server pushes into context #586)MAX_TOOL_PAGES(nowstella-mcp/src/client/ingest.rs:30, 1000) and the SSEMAX_BUFFERED_BYTES(stella-mcp/src/sse.rs, 8 MiB). Deferred because they are existing deliberate limits with their own rationale; lowering them risks breaking legitimately paginated servers, and the issue did not ask for it.stella-mcp/src/client/ingest.rs,stella-mcp/src/sse.rs(audit stella-mcp: nothing bounds what an untrusted MCP server pushes into the model's context #551, deferred by fix(stella-mcp): bound what an untrusted MCP server pushes into context #586)last_tool_idxprotection (currently exactly one tool message) to the whole streak. Deferred because it directly contradicts compaction's ratified behavior and its testred_loop_of_large_errors_is_reclaimable, whose entire premise is that a red loop of large repeated failures MUST be reclaimable. Trading context budget against loop evidence is an owner-level design decision.stella-core/src/compaction.rs(audit stella-core: compaction destroys the evidence loop detection requires, disabling the documented stuck-turn defense #554, deferred by fix(stella-core): keep loop-detection evidence across compaction #588)LoopDetectionConfigdefaults (e.g. the repeat threshold of 3) orEngineConfig::max_steps. Deferred: not asked for by the issue, the thresholds are documented as deliberately chosen, and changing them would mask rather than fix the identity problem — unverifiable without real-session data.stella-core(LoopDetectionConfig,EngineConfig::max_steps) (audit stella-core: compaction destroys the evidence loop detection requires, disabling the documented stuck-turn defense #554, deferred by fix(stella-core): keep loop-detection evidence across compaction #588)orderedto ~max_frames * 4(floored atLEXICAL_LIMIT) before the MMR pass and before frame construction, folding the discarded tail intodroppedas aFrameCountsummary; and cache the decoded vector index in the store keyed by fingerprint (invalidated onstore_embedding) instead of re-decoding every stored vector per recall. Deferred because the fix changes the contents of the drop report, which is the crate's observable contract (L-C5) and is covered by property tests — two coupled changes with no agreed drop-report shape.stella-context/src/retrieval.rs:180(audit Batched audit cleanup: area:context — 15 items #558, deferred by chore(stella-context): document the public surface and cache store_kinds #589) — landed in audit: five P0s, two red-main repairs, and one switch for every tool #707: the fused list is cut to 4xmax_framesbefore MMR and before any frame is built, and the tail is summarized into the drop report from the node rows soL-C5's partition still holds. (Witnessrecall_does_not_scale_quadratically_with_lifetime_memory_size— 7260 cosine calls with the bound removed.)upsertembed call at 64 to matchwarm_index, so a large delta cannot exceed a batch-limited embedding backend's request/payload limit once a real ONNX/hosted embedder replacesHashEmbedder. Deferred: it changes the embed loop's error/partial-progress semantics on the write path, the current code deliberately unzips to avoid doubling peak memory, and it is unverifiable without a real batch-limited backend.stella-context/src/writeback.rs:401(audit Batched audit cleanup: area:context — 15 items #558, deferred by chore(stella-context): document the public surface and cache store_kinds #589)max_live_turnscap returning 429 +Retry-After. Deferred: newServeConfigfields (constructed bytests/http.rsoutside the crate) plus a background reaper, andSession'sDropmust join or signal its thread — a substantial lifecycle change.stella-serve/src/server.rs:222,stella-serve/tests/http.rs(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 23, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)Sessionconstruction. Filed separately from the item above but the same defect; same rationale — newServeConfigfields plus a background reaper, and theSessionDropjoin/signal requirement is a substantial lifecycle change.stella-serve/src/server.rs:220(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 26, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)TooLargefrom a clean hangup inread_requestand reply 413/400 instead of conflating them; also revisit the 1 MiB body cap. Deferred: it changesread_request's signature and the response behavior of every malformed request — and the 1 MiB cap itself likely needs raising, which is a policy decision.stella-serve/src/http.rs:59(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 20, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)stella-observatory/src/lib.rs:202(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 15, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)stella-serve/src/http.rs:48(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 19, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)bytessemantics the dashboard page displays.stella-observatory/src/fsview.rs:170(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 14, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)turn.budget— the "consider" half of the item; themax_stepsclamp and the0 → 400rejection already landed. Deferred because whether a served turn may opt out of the host's spend ceiling is a product policy decision, not a cleanup.stella-serve/src/server.rs:206(audit Batched audit cleanup: area:core (1/2) — 31 items #559 item 27, deferred by chore(audit): batched area:core cleanup (7 of 31 items) #590)poll_videomaps every unrecognizedtask_statustoMediaJobState::Running, whoseis_terminal()is false, so a futureCANCELLED/TIMEOUT/EXPIREDmakes the job immortal and the agent-driven poll loop burns round trips and tokens forever. Two candidate designs: an age-aware terminal check onMediaJobStatus, or a caller-side deadline bounded byjob.submitted_atreportingFailed("provider never reported a terminal status within N minutes"). Deferred because picking the layer and theNis owner judgment, and the failure mode is not reproducible locally.stella-media/src/adapters/zai_video.rs:244(audit Batched audit cleanup: area:media — 10 items #565 item 2, deferred by chore(stella-media): fix the live-smoke gate, preview labels, branding #591)download_bytesbuffers an entire provider response into memory with no maximum;READ_TIMEOUT(60s) bounds only silence between reads, so a slow-but-steady body grows unbounded until the CLI is OOM-killed — host-memory DoS from a compromised or misbehaving vendor URL, on the shared image+video call site. Remediation: amax_bytesparameter or per-kind consts (e.g. 64 MiB image / 512 MiB video), aresponse.chunk()accumulation loop, an up-frontContent-Lengthcheck, andMediaError::Malformed("<provider> asset exceeded the N MiB download limit")on overflow. Deferred: the numbers are policy the owner sets, and the OOM failure mode is not reproducible in a local test.stella-media/src/http.rs:98(audit Batched audit cleanup: area:media — 10 items #565 item 6, deferred by chore(stella-media): fix the live-smoke gate, preview labels, branding #591) — landed in fix: the nine P0s from the backlog triage #658 (media download caps).ContextRecallPort::recall. There is none anywhere in the crate;tokio::join!(self.triage(...), recall_future)means a wedged embedding call or unresponsive CGP host hangs the whole pipeline before the first stage completes, with no event afterStage { ContextRecall }. Fix: addrecall_latency_ceiling: DurationtoPipelineConfig, wraprecall_futureintokio::time::timeout, degrade toRecall::default(). Deferred becausePipelineConfigis re-exported fromlib.rs(public API change) and the default ceiling is an owner judgement call.stella-pipeline/src/pipeline.rs:531(audit Batched audit cleanup: area:pipeline — 15 items #567, deferred by test(pipeline): name the work-item test modules for what they pin #594) — landed in fix: the nine P0s from the backlog triage #658:recall_latency_ceilingonPipelineConfig(which gains a field but is deliberately not madenon_exhaustive— all 30 construction sites use..Default::default()).gather_diffsubprocess fan-out. OneUntrackedNumstatsubprocess per changed untracked path, sequentially and uncapped, on every verification observation and after every revision turn — a turn creating many untracked files spawns thousands of git processes inside verify, repaid per revision × per candidate. Fix: cap the per-gather fan-out (~64) or add a batchedDiagnosticInvocation::UntrackedNumstatBatch { paths }variant. Deferred: both change observable diff-line accounting, the ~64 is a guessed constant, and the batched variant is a public enum change inports.rs.stella-pipeline/src/pipeline.rs:2149,stella-pipeline/src/ports.rs(audit Batched audit cleanup: area:pipeline — 15 items #567, deferred by test(pipeline): name the work-item test modules for what they pin #594) — landed in perf/reliability/accuracy: eight audit-backlog fixes, reconciled onto main's new durability contract #703 as bounded concurrency (16 in flight), deliberately not the truncating cap this row proposed: the count feeds the zero-diff guard and the diff-size budget, so dropping the tail would let a large untracked change slip under a budget it should trip.assemble_user_messageconcatenates every recalled frame's fullcontentwith no budget, no per-frame truncation and no frame-count cap;RecalledFrame::token_costis summed for theContextRecallevent but never bounds anything, so a mis-tuned recall port silently inflates every subsequent turn (it is inbase_messages, so N candidates × every revision pay it) and can push past the context window with no diagnostic. The same unbounded frames flow toplan::build_planner_promptandwitness::witness_prompt. Deferred because it changes prompt bytes — it moves every golden trajectory understella-pipeline/tests/fixtures/golden/and needs a deliberate budget default rather than an auditor's guess.stella-pipeline/src/pipeline.rs:2311(audit Batched audit cleanup: area:pipeline — 15 items #567, deferred by test(pipeline): name the work-item test modules for what they pin #594)server.rs:137-145spawns an unbounded task per accepted connection before auth. The exposure is confirmed, but the prescribed fix is hazardous: a semaphore bounding in-flight connections would be held for the whole lifetime of the long-lived SSE stream, starving theprovider-result/tool-resultPOSTs the same turn must deliver over separate connections — a self-deadlock of the reverse-RPC protocol. It also adds a publicServeConfigfield and requires picking timeout/concurrency values.stella-serve/src/http.rs:48,stella-serve/src/server.rs(audit Batched audit cleanup: area:core (2/2) — 31 items #560, deferred by chore(audit): apply the area:core (2/2) batch — 13 of 31 findings #595)ZipWriter. Deferred on scope (a signature change across five call sites), and the guard fires only pastu32::MAXbytes so no local test can exercise it — it fails the "verifiable locally" bar.stella-cli/src/export.rs:519(audit Batched audit cleanup: area:cli — 30 items #557, deferred by chore(stella-cli): apply the area:cli audit batch (14 of 30 items) #597)stella-cli/src/model_catalog.rs:620(audit Batched audit cleanup: area:cli — 30 items #557, deferred by chore(stella-cli): apply the area:cli audit batch (14 of 30 items) #597) — landed in perf/reliability/accuracy: eight audit-backlog fixes, reconciled onto main's new durability contract #703 at the proposed 15s per fetch.gh_json(proposed: 20s) so a hungghcall cannot stall the PR monitor. Deferred because the surrounding PR-monitor semantics — should a timeout be silent, or surface once? — are a product decision.stella-cli/src/command_deck.rs:2646(audit Batched audit cleanup: area:cli — 30 items #557, deferred by chore(stella-cli): apply the area:cli audit batch (14 of 30 items) #597) — landed in perf/reliability/accuracy: eight audit-backlog fixes, reconciled onto main's new durability contract #703 at the proposed 20s bound.CacheWarmth::from_elapsed(300, 300)reports expired at the TTL boundary whileis_cache_expired_rewrite(300, w, 300)returns false (strict>), so a scheduler consultingCacheWarmthand a counter consultingis_cache_expired_rewriteclassify the same moment differently. Both behaviors are explicitly test-pinned (warmth_counts_down_and_saturates_to_expired,expired_rewrite_needs_both_a_cold_gap_and_an_actual_write); picking one contract is a product decision.stella-model/src/cache_economics.rs:186(audit Batched audit cleanup: area:model — 18 items #566, deferred by fix(stella-model): land the area:model audit batch (#566) #598)SseDecoder.bufgrows without bound (noMAX_BUFFERED_EVENT_BYTES) when a provider or broken/hostile proxy streams megabytes without a blank line, andpoll's per-eventdrain(..boundary + delim_len)is quadratic; the fix needs the cap plus a consumed cursor. Deferred as a change to the decoder's error surface, which every adapter maps onto its ownProviderErrorclassification — choosing the cap value and the new terminal error is a design call with workspace-wide reach.stella-model/src/sse.rs:91(audit Batched audit cleanup: area:model — 18 items #566, deferred by fix(stella-model): land the area:model audit batch (#566) #598) — landed in fix: the nine P0s from the backlog triage #658:MAX_BUFFERED_EVENT_BYTESplus the consumed cursor, which also removed a second quadratic the witness found innext_event_boundary(the 50k-event test went 12.03s → 0.05s).Catalog::resolve→resolve_for(provider, &model)scoping change: a model slug that exists in the catalog only under a different provider id resolves toNone→cost_usd 0.0, which three new scoping witnesses now pin. Choosing betweenNone/0.0, a cross-provider fallback, or a hard error is a metering-policy decision.stella-model/src/anthropic.rs,stella-model/src/openai.rs,stella-model/src/zai.rs(audit Batched audit cleanup: area:model — 18 items #566, deferred by fix(stella-model): land the area:model audit batch (#566) #598)Store::prune, astella stats prunecommand, and truncation of oversizedToolResultpayloads. Deferred as out of scope for an audit edit: new library surface, new CLI surface, and a data-deletion policy.stella-store/src/lib.rs:726(audit Batched audit cleanup: area:store — 23 items #569, deferred by chore(stella-store): apply ten batched audit fixes from the area:store sweep #599) — landed in audit: five P0s, two red-main repairs, and one switch for every tool #707:Store::prune+stella stats prune, with the rowid-cursor hazard documented (store.dbrowids are externally anchored, so a naive prune silently loses telemetry).stella-store/src/lib.rs:1(audit Batched audit cleanup: area:store — 23 items #569, deferred by chore(stella-store): apply ten batched audit fixes from the area:store sweep #599)journal.jsonl. Only part (b) of the original item — theunsettled_promptssingle-reverse-pass rewrite — shipped. Bounding the size deletes durable resume state (the journal is half of whathas_statechecks) and the retention budget is a policy number nobody has set.stella-store/src/journal.rs:290(audit Batched audit cleanup: area:store — 23 items #569, deferred by chore(stella-store): apply ten batched audit fixes from the area:store sweep #599)references()'s corpus scan. It opens andread_to_strings every file in the index until it accumulates 50 hits, so a rare or zero-match identifier costs a full synchronous read of the entire indexed corpus on the caller's thread — tens of thousands of syscalls, hundreds of MB, per call on a 10k-file workspace. Three mutually-exclusive designs were offered with no bound named for any of them: cap files opened per call, prefilter to files whose symbol rows already mention the name, or back it with an FTS5 index. All three change observable results; picking both the design and the cap is a retrieval-quality decision, and the FTS5 option is a new index, not a cleanup.stella-graph/src/frames.rs:68(audit Batched audit cleanup: area:graph — 15 items #563, deferred by perf(stella-graph): bound the audit's unbounded loops, scans, and walks #600)SessionModel::files/FileLedger::records—touch_fileclones the full diff and pushes unboundedly. Confirmed, but all three sub-changes alter the pure fold the replay-determinism proptest pins, and the eviction bound plus the user-visibleEvictedmarker are product decisions with no obviously right value.stella-tui/src/model.rs:774(audit Batched audit cleanup: area:tui — 26 items #571 item 10, deferred by chore(stella-tui): clear the area:tui audit batch (9 items) #602)#[cfg(unix)]/#[cfg(not(unix))]split whose non-unix branch could not be compiled locally (shell.rsshows the crate really does maintain a separate Windows path), and the prune policy (age vs count, threshold) is an unspecified decision on a directory other crates read viadefault_attachments_dir().stella-tui/src/clipboard.rs:70,stella-tui/src/shell.rs(audit Batched audit cleanup: area:tui — 26 items #571 item 14, deferred by chore(stella-tui): clear the area:tui audit batch (9 items) #602)web_fetch's clamp. Either align bash'sMAX_OUTPUT_BYTES = 100_000with the sharedexec::MAX_OUTPUT_BYTES = 30_000, or document why a shell needs 3.3x the budget of a build; and lowerweb_fetch's 400,000-char upper clamp to something a context window can absorb (~120k chars). An explicit either/or on a user-visible output budget: lowering the cap changes what every bash-enabled session sees, and writing the justification for keeping it requires the owners' actual rationale.stella-tools/src/bash.rs:31,stella-tools/src/exec.rs,stella-tools/src/web.rs(audit Batched audit cleanup: area:tools — 26 items #570, deferred by chore(stella-tools): bound tool output, harden sub-resource auth, prune the process table #603)Why these are together
None of these is an open engineering question — every one has a known fix that was written down and then stopped at the same wall: a constant nobody has authority to invent, or a fork between two defensible behaviors. They span nine crates and eleven audit issues, but they are one unit of work for the person who can rule on them: sit down once with the whole table, write a number in each row, and the code changes become mechanical follow-ups that can be split however is convenient. Deciding them one at a time is what has kept them open, because each in isolation looks too small to justify a policy conversation and too policy-shaped to settle in a bug fix.
Deferred during the #546 audit remediation. Put
Closes #<this issue>in the PR description and as a commit trailer when it lands.