fix(doctor): changelog and isolate probeRoots env in #899 tests

Restore CODEBUFF_DATA_DIR / VIBE_HOME after the batch-2 cases so later
suites cannot inherit a leftover override.
This commit is contained in:
Aditya Vikram Singh 2026-08-23 18:53:45 +05:30
parent 57709c69c6
commit caba98817b
2 changed files with 13 additions and 1 deletions

View file

@ -42,6 +42,8 @@
- **The resident `codeburn serve` child.** The first real panel request is also the cache warm-up, so startup never runs an artificial warm-up query beside a duplicate one-shot child; each served command carries its own read-only option allowlist, and anything outside it falls back to a normal spawn; the child exits when its stdin closes, so it can never outlive the app. Requests whose response exceeds the 16 MiB frame limit still replace the child, but that deliberate kill no longer spends the resident's unexpected-death budget. (#972)
### Fixed
- **`codeburn doctor` now probes the five remaining fixed-location providers.** `codebuff`, `devin`, `gemini`, `kiro`, and `mistral-vibe` implement `probeRoots()` through the same resolvers discovery uses, so a silent zero is distinguishable from a missing install. Codebuff reports all three manicode channels unless a factory or `CODEBUFF_DATA_DIR` pins one; Devin reports `transcripts` plus `sessions.db`, not the parent; Gemini reports only `~/.gemini/tmp`; Kiro reports pre-filter candidates (empty CLI/v2 skipped, empty agent/workspace fall back); Mistral Vibe reports the joined sessions dir. Missing defaults still appear. Thanks @therickfactr. (#899)
- **Subscription SKUs are classified from real product ids, and a false-positive built-in can be opted out.** `codex-auto-review` consumes ordinary Codex usage ([openai/codex#32224](https://github.com/openai/codex/issues/32224)) and is priced as GPT-5.5 on #1056, so treating it as $0 hid real spend — it left the flat-rate list. Warp's product id is `auto`, not the synthetic `warp`. `kimi-for-coding-highspeed` (the SKU #968 was filed around) is now honestly $0. `big-pickle` was dropped: it appears under OpenCode, not as a cited ClinePass codename. `codeburn model-flat-rate --remove` now opts out of a built-in, so a wrong classifier entry can warn again without waiting for a release. The daily-cache config hash now always includes the flat-rate section (even when empty), so the first run after upgrade re-derives every stored day once from the warm session cache. (#968, #1050)
- **Codex MCP and skill usage is attributed from every shape Codex records a shell command in.** `mcp-cli call <server> <tool>` was only recognized when the command arrived as `function_call` arguments (#656). Codex has two other shapes for the same exec: its custom-tool transport records the shell tool as a `custom_tool_call` whose payload is an `input` program rather than `arguments`, and its item model repeats a finished command as `event_msg`/`item_completed` carrying a `CommandExecution` item with an argv `command`. Both reached the Bash counter and neither reached the matcher, so a CLI-wrapped MCP call stayed absent from the MCP breakdown exactly as before the fix. All three shapes now feed one classification pipeline. The same pipeline learns skills: Codex has no skill tool, so loading one is a shell read of the skill's `SKILL.md`, and those reads landed entirely under Bash with the Skills dimension empty. A read counts as a skill load only when the command segment starts with a file-reading binary (`cat`/`bat`/`sed`/`head`/`tail`/`less`/`more`) and the path it reads ends in `<name>/SKILL.md`; the skill is `<name>`, the same key `pi` derives for a native skill read (#588) and the same vocabulary the Claude parser records from the `Skill` tool. A `grep`/`rg`/`ls` that merely mentions a `SKILL.md` is a search near the file, not a skill load, and stays plain Bash. This is attribution only — no call, token or cost figure moves, and a command carried by both a response item and an item-model item is attributed once. On a 1,397-rollout corpus: Skills went from empty to 7 skills over 35 turns (55 attributions), Bash was unchanged at 42,170, and cost, calls, tokens, sessions, daily, models and projects came back identical. Cached Codex sessions re-parse once (`CODEX_CACHE_VERSION` 13 → 14 and the codex parse version both move; without them the fix is invisible on a warm cache). Thanks @chr-evensen. (#478)
- **`gpt-5.6-codex` and `gpt-5.6-codex-max` now have their own pricing rows.** Neither id is in LiteLLM yet, and both were missing from the bundled snapshot — flagged during #1075 verification on a real corpus (285 sessions, 5,446 calls). `getModelCosts` already resolved both through the `gpt-5.6` prefix fallback, so live pricing was already correct once a session priced fresh; every prior Codex-suffixed id LiteLLM does carry bills identically to its bare-model sibling of the same generation (`gpt-5-codex` == `gpt-5`, `gpt-5.1-codex` == `gpt-5.1-codex-max` == `gpt-5.1`, `gpt-5.2-codex` == `gpt-5.2`, `gpt-5.3-codex` == `gpt-5.3`), which is the evidence both new rows mirror rather than inventing a rate. The gap that does not self-heal is the daily cache: it has no per-provider invalidation, so a day finalized while either id had no billable rate keeps that $0 forever. Raising `MIN_SUPPORTED_VERSION` (v23 -> v24) forces the one-time re-derivation, a lossless no-op for days already correct. (#1077)

View file

@ -1,4 +1,4 @@
import { describe, it, expect } from 'vitest'
import { afterEach, describe, it, expect } from 'vitest'
import { isAbsolute, join } from 'path'
import { homedir } from 'os'
@ -117,6 +117,16 @@ describe('probeRoots mirrors discovery resolution (Tier 2, batch 1)', () => {
// #899 Tier 2, batch 2. Same contract as batch 1: probeRoots() is the exact
// discovery-root set after the same resolution, pinned as full objects.
describe('probeRoots mirrors discovery resolution (Tier 2, batch 2)', () => {
const originalCodebuffDataDir = process.env['CODEBUFF_DATA_DIR']
const originalVibeHome = process.env['VIBE_HOME']
afterEach(() => {
if (originalCodebuffDataDir === undefined) delete process.env['CODEBUFF_DATA_DIR']
else process.env['CODEBUFF_DATA_DIR'] = originalCodebuffDataDir
if (originalVibeHome === undefined) delete process.env['VIBE_HOME']
else process.env['VIBE_HOME'] = originalVibeHome
})
it('codebuff reports all three CHANNELS on default, and empty factory is unset', async () => {
const expected = [
{ path: join(homedir(), '.config', 'manicode'), label: 'chats' },