Skip to content

Releases: HuginnIndustries/CodeCartographer

v0.22.2

Choose a tag to compare

@github-actions github-actions released this 13 Sep 07:27
1dadcb8

One defect from the 0.22.1 live verification, fixed with its own live verification (#324).

Fixed

  • Two collects on one Broad-Side run submit each post-pass and the truncation retry once. A client's request timeout fired mid-wait, the client exited, the MCP server kept polling and submitted synthesis and triage on its own, and a second collect on the same run submitted its own pair — four paid post-passes for one run, with state.json written by whichever process persisted last (#322). Before a collect spends on a slot of the run it now claims it under the state lock: an unclaimed slot is marked submitted before any network call; a slot another collect holds is adopted — polled when it has a batch id, reported as in flight elsewhere when it does not. The retry is recorded on the run (run.retry) so the claim covers it too, and a collect that polled nothing reads the saved results back and retries under the claim if the collect that saved them never did. Writes inside collect merge slot by slot, keeping whichever side is further along, instead of replacing the run wholesale.

  • A server whose client is gone stops polling and submits nothing further. runBroadsideCollect takes an AbortSignal; the poll loop returns at once when it fires, waking from its interval rather than sleeping it out, and accepted batches keep running for the next collect to claim. The MCP server fires it on transport close and on the end of stdin — the SDK's stdio transport watches data and error only and never notices a client exiting. Verified live: a server whose client gave up after 30 s exited within 3 s (it used to outlive the client by a quarter of an hour), and the next collect submitted exactly one synthesis and one triage.

  • The collect report names a post-pass still running, held by another collect, or failed with its reason. A run reported "completed" used to say nothing about a synthesis whose batch was still running when the wait ran out. Both post-passes are now polled together against the shared deadline; polled in turn, the first could spend the whole budget and leave the second a single poll.

  • wait_seconds is documented as bounded by the host's tool-call timeout, with what happens when the host gives up first (tool description, MCP quickstart, README).

v0.22.1

Choose a tag to compare

@github-actions github-actions released this 13 Sep 06:17
193b3ae

What running 0.22.0 against the real OpenRouter Batch API and a real Pi session turned up, in one PR (#320). Five defects, none of which a mocked fetcher had a reason to show; the first changes what every lens request asks a model for.

Changed

  • Every lens request asks for low reasoning effort instead of a token cap. The cap (reasoning.max_tokens at a quarter of the lens budget) is not honoured everywhere: Gemini 3.x models take a thinking level, not a budget. Measured on google/gemini-3.8-flash:batch, defect lens, same slice — under a 5,800-token cap the model reasoned 5,218 tokens and truncated; the #133 retry doubled max_tokens and the thinking doubled with it (11,518 tokens, truncated again, twice the price for no JSON); effort: low reasoned 0 tokens, finished cleanly, and cost a twelfth as much. effort is the one control OpenRouter translates for every provider, so it is the default (BROADSIDE_DEFAULT_REASONING). Still never enabled: false, which that endpoint refuses outright. config.yaml's reasoning: block still overrides it.

  • A truncated slice is retried at low effort as well as a doubled cap. The cutoff is usually thinking, so the retry now replaces a configured token cap with effort: low (OpenRouter refuses a request carrying both) and lowers a higher effort; an effort already at or below low, or an explicit enabled: false, is left alone. Verified live: a slice truncated at 5,756 reasoning tokens under effort: high was recovered by one retry batch with 0 reasoning tokens.

  • A headless /codecarto-broadside submit behaves like the MCP surface. Under pi -p there is no dialog and the confirm stub answered "no" to every estimate, so a run well under max_cost reported "Broad-Side cancelled. Nothing was submitted." Without a UI, an estimate within the cap is approved by the cap itself, one over it is refused with a message that says so, and the per-lens breakdown is printed to stderr either way.

Fixed

  • A reasoning: block with both effort and max_tokens refuses the run. OpenRouter accepts one or the other; a request carrying both is refused per request after the batch is accepted, so every lens failed at $0 with the reason buried in each result's error — and the shipped config.yaml comment showed the two keys together. The loader now throws BroadsideConfigError for such a file, and the comment shows the three forms as alternatives.

  • A skipped lens names the globs that matched nothing. The security lens reads server/**, **/auth*, **/middleware/**; a JavaScript service whose server lives at src/server.js matched none of them and the report said only "skipped (0 request(s))", which read as an empty repository. The submit report and status now carry the reason (the globs, a language with no patterns, or an incremental run with none of the lens's files changed). #319 tracks the lens globs themselves.

  • A run with no batch behind it is failed, not in-flight. When every lens was skipped or refused, the run kept its initial status and status listed it above the completed runs with synthesis and triage pending forever.

  • The MCP wait output reports a lens's status once per change. A submit with wait_seconds returned one in_progress (0/1) line per poll per lens — twenty-six of them over a four-minute wait — before the result. #321.

  • The Pi spend dialog mentions incremental only when it was requested, and names the real reason a requested run did not apply, in the sentence the MCP report already used. It used to print "Incremental was requested but the tree is dirty" on every dirty tree.

v0.22.0

Choose a tag to compare

@github-actions github-actions released this 13 Sep 00:55
0211e4a

The remaining open issues after the self-audit, in one pass: the last Broad-Side roadmap item (#141), the retry serialization it uncovered (#206), the Codex verification gap (#218), and the repository's own workspace state (#276). A minor because codecarto_broadside and /codecarto-broadside gain parameters and the models listing changes shape.

Added

  • Broad-Side model selection on both surfaces. core/ already routed lenses through config.yaml's lens_models, but neither surface took a model, so the models action helped choose one that could then only be applied by hand-editing the file. codecarto_broadside submit takes model and lens_models (validated before anything is priced); /codecarto-broadside takes --model=ID and a repeatable --lens-model=LENS:ID, split on the first colon since a batch id carries one. A lens set both on the call and in the file takes the call's. Either path reaches the existing pre-flight — priced from the catalog, refused without structured-output support, clamped to the model's ceiling. #141.

  • The models listing is advisory, and says so. OpenRouter's catalog returns a :batch id for models whose Batch API then refuses the job (does not have a :batch endpoint), with nothing in the entry to tell them apart. A submit now records what the provider said about each model it posted to in broadside/batch-endpoints.json — accepted, or refused for that reason; a quota or network failure says nothing about the endpoint — and the listing tags rows [no batch endpoint, refused <date>] / [batch OK <date>], untried rows carrying no tag. The MCP result carries catalogAdvisory: true and the endpoints map. #141.

  • Codex can be verified headlessly. codex exec refuses MCP tool calls under its forced approval_policy=never; the per-server key mcp_servers.<name>.default_tools_approval_mode = "approve" lifts it without disabling the shell sandbox. Verified against this build (codecarto_init then codecarto_status through Codex 0.154.0 over stdio, workspace on disk, tool text echoed). CONTRIBUTING carries the recipe and what a failed approval looks like; docs/client-surfaces.md replaces the four-release "unknown" row with the run. #218.

Changed

  • The truncation retry runs as one batch per model. Each truncated slice used to be submitted as its own single-request batch and polled to terminal before the next was submitted — the serialization #136 removed from the lens pass, still present here — so a model that truncated 11 of 13 slices turned one collect into eleven sequential round trips and eleven of the sixteen concurrent-job quota. Bumped requests are now grouped by the model they must run on, submitted as one batch per model, polled together against the caller's remaining deadline, and matched back by custom_id; per-slice bookkeeping is unchanged, the batch's cost is added once, and the onStatus label is retry:<model>. #206.

  • Broad-Side refusals say what to do. A rejected lens in the submit report was a bare rejected; it now carries the reason, and the two refusals a run meets in practice are explained: the missing batch endpoint above, and job-submission-count — the per-account limit on concurrent batch jobs, which one job per lens fills after a few runs on the same key. The quota can also fill after acceptance, in which case the batch completes with every request failed and the lens read completed, 0 result(s) with the reason buried in <id>.error.json; collect now reports all N request(s) failed: <reason> on that lens. #141.

  • This repository's .codecarto/ workspace files are pristine. The tracked BACKLOG.md, THREAD_LOG.md, and closeouts/ carried a 2026-05-02 framework session. Init never copied them and the tarball negates them, so no user received them; but a fresh clone reported an existing workspace, the GUIDE's first-time heuristic was wrong from the first call, and the self-audit appended to that session's log. They now equal their templates, closeouts/ holds a .gitkeep, and the framework's own history lives in docs/history/. No ignore-rule change: a user's backlog, log, and closeouts are their project's history and stay tracked. #276.

v0.21.0

Choose a tag to compare

@github-actions github-actions released this 12 Sep 09:47
a5840da

The third and last release from the self-audit (self-audit/, issues #223#279): the six remaining highs, every low group, and the template notes from driving the pipeline. One issue stays open — #276, whether this repository should keep tracking its own .codecarto/ workspace state — because that is a maintainer's call, not a defect. A minor because three guardrails now refuse where they used to proceed: a pipeline that cannot finish is reported as stuck rather than complete, Broad-Side ships with a spend cap, and an unreadable Broad-Side config or state file stops the run instead of being read as empty.

Added

  • codecarto_next and codecarto_phase take unattended: true — the MCP spelling of Pi's --auto. The reimplementation-spec phase's Strategic Alignment Hook then defaults to language-agnostic and records selection: auto-default instead of asking which variant to build; an autonomous host driving the audit had nobody to ask and improvised. The prompt is byte-identical to what Pi's --auto builds. #270.

  • Broad-Side collect can target a run: run_id on the MCP tool, --run=<id> on the slash command (status lists the ids). With two runs in flight, collect always read the most recent, so an older run's paid results were unreachable once a newer submit existed. #268.

  • The pipeline engine has a third answer. resolvePipelineOutcome returns eligible, complete, or stuck — a stuck pipeline names each blocked phase and the dependency keeping it there, whether that dependency is missing from the pipeline or itself blocked (a cycle). isPipelineComplete and describeStuckPipeline alongside. #228.

  • Both defect templates gain a ## Runtime probes section, and the severity rubric says what a probe does: it settles the evidence level and may move the severity either way, and never promotes a finding for having been confirmed. The mechanical scan's prerequisites name the manifest, compiler config, CI workflows, and ignore files its pass 6 is about; the semantic scan may read a trusted dependency's pinned source; the porting phase reads each scan's probes first. #275, #279.

Changed

  • A pipeline that cannot finish is stuck, not complete. A DAG whose remaining phase depended on a phase the file never declared reported "Phase: complete", "Progress: 1/2", and unlocked the post-pipeline skills and amendments. Status on both surfaces now shows Pipeline state: stuck with the sentence; codecarto_next and /codecarto-next refuse (an error, since a host looping on next would read a text answer as done); skills, list-skills, and amendments refuse; completion and a switch leave the cursor on the first blocked phase rather than writing the terminal routing; the auto runner ends with a stuck outcome of its own. #228.

  • Broad-Side ships with a spend cap of $1.00. The MCP surface cannot ask a human before spending and had no default limit, so a host calling submit with the stock config spent whatever the estimate came to. The cap applies wherever neither config.yaml nor the call sets one; an explicit max_cost: 0, in the file or on the call, is no limit; over the cap submit refuses with the per-lens breakdown unless forced or approved, exactly as a configured cap did. #231.

  • An unreadable Broad-Side config or state file refuses the run. A config.yaml that existed but failed to parse was treated like an absent one — defaults, no cap, no lens routing, no message — so a typo removed the user's own guard; it now throws BroadsideConfigError, submit/collect/models refuse on both surfaces, and status answers with a warning. A state.json that could not be parsed was read as empty and the next checkpoint wrote that empty state over it, losing the batch ids of every paid, in-flight run; it now throws BroadsideStateError after preserving the file as state.json.corrupt-<hash>, and nothing writes over it until someone looks. #232, #233.

  • wait_seconds: 0 means what the documentation said. An explicit 0 — and the default, which is 0 — became undefined on the way into core and, there, the 25-minute poll budget, so every collect without a wait polled for half an hour. Both wrappers pass 0 through; collect polls each in-flight batch once and returns. #230.

  • Phase prompts after the first phase mark the three framework-owned reads. GUIDE.md and the handoff template are annotated as unchanged since the last phase and skimmable; status.yaml as rewritten by completion. Same text on both surfaces. #274.

  • Template notes. The spec template's Carry-Forward comment states completion's rule (a later active phase, or post_pipeline) instead of naming targets completion refuses (#271); VALIDATE.md and GUIDE.md name UTC as the clock for every date, so a host's **Validated by:** line stops landing a day before its closeout (#272); the protocols phase owns the storage-format catalog and contracts names formats as behavior (#277); the porting template's Defect Synthesis lets completeness win over "one screen", grouping lows by root (#278).

Fixed

  • A digit-named repository bricked its workspace. The YAML emitter wrote "2048", "true", "null", "1.5" bare and the reader returned a number, a boolean, or nothing; a repository whose directory is all digits got project_name: 2048 and the next load threw project_name?.trim is not a function. The emitter quotes any string the reader would coerce (the reader is the oracle), the readers coerce scalar text instead of assuming it, and a status.yaml written before the fix loads and is rewritten quoted on its next write. A seeded round-trip property test pins the pair. #225.

  • The first decision row completion appends to DECISIONS.md is separated from the paragraph above it, so renderers stop folding it into the prose. #273.

  • User-fixable workspace errors are InvalidRequest, not InternalError, on every MCP tool: an unparseable status.yaml, a missing pipeline:, a pipeline file that is not there. Hosts no longer treat a config problem as a server bug to retry. #263.

  • The child session's phase-id match admits every id assertSafePhaseId admits (., _, capitals), so a custom pipeline using such ids keeps its bash block, write confinement, and checkpointing. Every git subprocess carries the 30 s timeout every fetch already had. Pi's library-init expands ~ with the shared helper. #263, #264.

  • The catalog cache stamps each model separately (schema 3, with a schema check on read that the writer never had), so fetching one model no longer renews every other cached price's 24 h TTL. The batch statuses collect never polls again are one constant instead of three literal copies. #266.

  • The library list's source_repo filter, docs drift, and the trusted-host posture. docs/client-surfaces.md states what the server trusts and does not; the README no longer claims a JavaScript-free dashboard or session-file links that never rendered; MANUAL names the right default pipeline and five evidence levels; the quickstart describes what each scan covers; INDEX.md stops naming a CLI that does not exist; the codecarto_complete and api_key descriptions say what happens. #265, #267.

v0.20.0

Choose a tag to compare

@github-actions github-actions released this 12 Sep 06:05
7a6aac4

The second release from the self-audit (self-audit/, issues #223#279). 0.19.6 shipped the four fixes the review ranked first; this one closes every medium in its queue, #235#261, as seventeen PRs (#287#303). A minor rather than a patch because three things changed on purpose: library-init no longer switches the MCP publish gate on, a repository Broad-Side cannot scan is refused before it costs anything, and a config file the loader cannot use now stops the library tools instead of being quietly dropped.

Added

  • Broad-Side redacts secrets before upload (#252). Files named like credential stores — .env*, *.pem/*.key/*.p12, id_rsa*, .npmrc/.netrc, credentials.json, secrets.yaml, *.tfvars, service-account JSON — are left out of every lens by name, and well-known secret shapes in every other file (private-key blocks, AWS/GitHub/OpenAI/OpenRouter/Anthropic/Stripe/Slack/Google keys, JWTs, quoted values assigned to password/secret/token-style keys, passwords inside URLs) become [REDACTED:<kind>] before the slice is built. The marker keeps a hardcoded credential visible to the security lens; the value stays home. The submit report says what the pass did, the run records it, and redact_secrets: false in broadside/config.yaml turns the content pass off. Low-false-positive patterns only: a safety net for an accidental upload, not a secret scanner. core/secrets.ts.

  • Config faults are reported. A ~/.codecarto/config.yaml or .codecarto/workflow/config.yaml that fails to parse, a section that is not a mapping, or a key of the wrong type used to vanish without a message. The loader now records each fault in config.problems, at the granularity of the fault, and codecarto_config / /codecarto-config list them; a phase run warns and continues; the synthesis preflight names them ahead of its "no library.path" advice. #242.

  • Status names a complete phase whose report is not on disk. status.yaml is committed and findings are gitignored by default, so a fresh clone said "6/7 complete" about reports it did not have. codecarto_status and /codecarto-status now list every such phase and its path, and README's new "What to commit" section states the policy and the per-workspace opt-out. The default stays ignore-by-default. #259.

  • A pipeline switch reports what it moved. The MCP result carries currentPhase and dangling; both surfaces print each re-routed carry-forward with its original target (see Changed). #237.

Changed

  • codecarto_library_init and /codecarto-library-init write only what they were asked for: library.path, and library.namespace when given. They no longer write publish_confirm: true, which had switched on the MCP refuse-unless-confirmed gate for every host that had never configured it — the opt-in #162 set up, defeated by the tool that creates the library. An existing publish_confirm, orchestrator block, or namespace is left as it was, and a config file that cannot be parsed is never rewritten. Set publish_confirm yourself to gate the MCP server. #244.

  • The library tools refuse while the configuration has problems. codecarto_publish, codecarto_library_list, codecarto_library_reindex, and /codecarto-publish answer from the config — its path, namespace, and confirm gate — so a file that could not be used in full is a refusal naming the file and key, not a silent fallback to whatever the other layer says. A relative library.path is one such problem: it resolved against wherever the server or Pi was launched, so the library moved with the launch directory; it is refused, and absolute or ~-prefixed paths are unchanged. #242, #243.

  • Broad-Side scans one snapshot and refuses what it cannot scan. The file list came from git ls-tree HEAD while contents came from the working tree, so a run mixed a committed list with uncommitted contents and never saw an untracked file; both now come from the working tree (ignore rules applied, files deleted on disk left out), and the run records the snapshot source beside its HEAD and dirty flag. Language detection takes every manifest present as a candidate — package.json covers TypeScript and JavaScript, decided by the counts — and picks the one with the most source files, so a Python service with a docs package.json is Python. A repository whose language the lenses cannot tell, or whose detected language has no source files, is refused before pricing or the network; it used to fall through to Go's globs and pay for empty batches. #248, #250.

  • A pipeline switch recomputes the cursor and re-routes carry-forwards whose target it dropped. After completing a phase and switching, status.yaml said "Begin architecture phase" beside a complete architecture record; the cursor is now derived from the engine, through the same code completion uses. A carry-forward targeting a phase the new pipeline lacks moves to post_pipeline with its source phase and a note naming the dropped target, so an amendment can close it — the rule completion already applies to such a target in a handoff. #236, #237.

  • Init on the packaged template itself is an existing workspace like any other. A checkout's .codecarto/ is the template init copies from and a live workspace at once; init used to skip both the refusal and the backup for it and reset status.yaml in place — the self-audit hit exactly that. MCP now refuses without force: true and Pi asks; a forced re-init moves the session state out file by file into .codecarto-backup-TIMESTAMP/, since the template cannot be renamed away, and leaves the framework files in place. #245.

  • Text an earlier session wrote is quoted in the next prompt as data. Routed item descriptions, re-triage questions, upstream coverage bullets, and library headlines were spliced into phase prompts as plain lines — indistinguishable from instructions, able to break their list with a newline, unbounded. Each is now one line, capped at 400 chars with a note that the full text is in its file, inside «…», under a header that says what «…» means. GUIDE.md's session-start step says the same. #253.

  • PhaseRunResult no longer carries the child session, and PhaseRunCallbacks.onSessionCreated is gone. Nothing read either; every child AgentSession is now disposed once its work is done (see Fixed). #256.

Fixed

  • A skill name could reach outside skills/. Both surfaces built .codecarto/skills/<name>/SKILL.md from the supplied name and served it when that file existed; every findings directory ships a SKILL.md, so ../findings/architecture spliced a phase skill into the post-pipeline prompt. Names now resolve against the installed list only. #235.

  • A PARTIAL row that names the entry tracking it no longer becomes a second open question. VALIDATE.md already asks the evidence cell to name the routing entry; completion ignored it and registered a needs-maintainer-decision question for every PARTIAL row, so a routed gap was closed twice. A row that names nothing still becomes a question. Moving the step after the handoff also closes a collision: gap questions and id-less handoff questions were both numbered from oq-<phase>-1, and the handoff's replaced the gap outright. #239.

  • The library tools' optional cwd is validated before it becomes a containment root. codecarto_publish joined a raw cwd onto .codecarto as an allowed root for spec_path, read the spec against it, and only then refused a relative cwd; a relative path resolves against the MCP server process's working directory. cwd is now checked first — absolute and existing — by publish, list, and reindex alike. #241.

  • The YAML reader accepts the layouts a model actually writes, and every parse error names the line and construct. A list at the same column as its key read as null and, at the top level, silently dropped every line after it; a plain scalar wrapped onto more-indented lines (a long closeout_summary), a scalar starting on the line after its key, and a sequence of sequences all failed with "Invalid YAML indentation"; a tab in the indentation produced the key "". All parse now, folding like a > block, and a failure reads YAML line N: <what was expected> — "<the line>". Content after the top-level block is an error rather than unread. #246.

  • **Overall:** PASS (6/6) passes validation. The verdict had to be exactly PASS or PASS WITH GAPS; a count, a period, or bold around it failed the phase with the bare "Validation overall result is FAIL". The verdict is now whatever the value starts with, and an unreadable, missing, or FAIL line each produce an error that quotes it. #247.

  • Broad-Side's estimate covers what it sends. The entry point and manifest were read whole into the architecture prompt while that lens was estimated at a flat 6,000 chars; both reads are capped at 20,000 chars with a marker, and the estimate sizes every request from the user prompt it would actually send. #249.

  • Broad-Side names an auth failure, a dead network, and a failing gateway. A 401 or 403 from the model catalog fell into the built-in pricing fallback (silently, for the default model) or "could not resolve per-token pricing"; the poller swallowed every fetch error until its budget ran out and returned "timeout". The catalog lookup now throws BroadsideAuthError naming the status and the provider's message before anything is priced or posted, other failures name their cause, a timeout without one good poll carries its last error, and the collect report prints a lens's error on its line. #251.

  • MCP re-renders the dashboard on a pipeline switch. Pi did and MCP did not, so the file showed the old pipeline until the next completion. The writer moved from the Pi extension into core/ — the MCP server had been importing it across wrappers — with a re-export shim at the o...

Read more

v0.19.6

Choose a tag to compare

@github-actions github-actions released this 12 Sep 03:15
702581e

The first release after running the deep-audit pipeline on this repository itself (self-audit/, issues #223#279). This ships the four fixes ranked first in that review.

Fixed

  • A write inside .codecarto/ could land outside it. The Pi write sandbox resolved a not-yet-existing target lexically, so a symlinked directory inside the workspace that pointed elsewhere let a new file through, and link/.. was collapsed before the disk was consulted. Containment now follows every existing path component through realpath and appends the unborn tail (resolveExistingPrefix), in both the extension hook and the phase child-session hook. #223.

  • codecarto-init from a checkout copied the framework's own findings into every new workspace, and npm installs shipped no .gitignore. This repository's .codecarto/ is the template and CodeCartographer's live workspace at once, so a finished phase in the checkout seeded every new workspace with another project's report (validation then passed on it, and the test suite failed 18/696 once one phase was complete). Init and scaffold refresh now leave behind every path a packaged pipeline declares as an output, plus scratch contents, closeouts, the dashboard, status, usage, and stray lock files. npm never packs a file named .gitignore, so the rules ship as templates/gitignore and init and refresh write .gitignore from it when the workspace has none; the rules also gain the two deep-audit reports they were missing. #224, #229, #258, #269.

  • Eleven writers used <pid>.<Date.now()> temp names, and the status lock released whoever's lock was there. Two writers hitting one file inside a millisecond collided: one rename failed or clobbered the other, which lost concurrent usage-log appends and library publishes outright. Every framework file now lands through one atomicWriteFile with a unique suffix. The lock file carries a per-acquisition token and release removes it only while that token is present, so a holder whose stale lock was broken can no longer delete the new holder's lock. Usage appends run under the lock and refuse to rewrite a log that does not parse; publishes hold a library-wide lock so two concurrent publishes of one slug land as v1 and v2. #226, #227, #238, #240.

  • Completion wrote its closeout, index line, and decision rows before status.yaml was committed. A commit that failed left every artifact asserting a completion that never happened. updateStatusAtomically now takes an afterCommit step that runs once the rename has landed; completion and amendment write their artifacts there, and a failure after the commit is reported with the note that re-running regenerates them. #234.

v0.19.5

Choose a tag to compare

@github-actions github-actions released this 11 Sep 23:45
b78a823

Fixed

  • In a non-interactive Pi session, a command that worked and a command that silently refused looked identical. Under pi -p, ctx.hasUI is false and ctx.ui.notify is a silent no-op, so /codecarto-status, /codecarto-usage and /codecarto-list-skills — whose only output is a notification — exited 0 having printed nothing. So did the codecartoModeActive refusal that every command goes through when no /codecarto-init or /codecarto-open has run, which is the silence that let #180 go undiagnosed for months. It also meant status and usage could not be verified headlessly at all: CONTRIBUTING said to check a side effect, and those two have none. Ninety-eight notification sites across the extension now go through one notifyCtx (extensions/codecarto/notify.ts) that uses the TUI when there is one, writes a single [codecarto] <level>: <message> line to stderr when there is not, and drops the message only when the ctx is stale. Stderr rather than stdout because --mode json owns stdout for its event stream and prose there would corrupt it; verified that stdout stays a clean stream of JSON events while the notices arrive on stderr alone. The interactive TUI is untouched — the parity suite runs every command with a UI and pins that. Code that runs inside the phase sub-agent (phase-compaction.ts) deliberately keeps its own if (ctx.hasUI) guards: it sees hasUI === false even while the parent TUI is on screen, and a stderr write there would put text into a live terminal. #219.

v0.19.4

Choose a tag to compare

@github-actions github-actions released this 10 Sep 19:05
5f46469

Fixed

  • Block scalars still failed inside a sequence, so 0.19.3 did not actually finish #211. The fix landed on mapping values (key: >-) and not on sequence items (- >-), and the second is the shape an LLM reaches for when it writes a list of prose entries — a decisions: list with one folded entry per decision. A real run hit it the same day 0.19.3 shipped: the architecture phase ran for 25 turns and 73 tool uses, wrote a PASS WITH GAPS artifact, and then auto-completion died on Invalid YAML indentation near: The architecture map treats the legacy filesystem pipeline…. The phase stayed pending, the dashboard showed 0/7 with the artifact and token usage recorded beside it, and the auto run stopped one phase in. - |- was never supported either, so this was not a regression from #211 so much as a hole it left. The block-scalar body reader is now one shared routine used by both the mapping and sequence paths rather than logic inlined in one of them, so the two cannot diverge again.

Changed

  • /codecarto-next's flags now explain themselves. --auto and --llm-steer are independent — one decides how many phases run, the other decides what prompt each gets — and the combination that suits most full runs, --auto --llm-steer, was not guessable from a completion list that showed four bare flag names and a description that just listed them again. Completions now carry a sentence each (the AutocompleteItem.description field was there all along, unused), --strict is offered only once --auto is present rather than inviting the one combination the parser rejects, /codecarto-init names the full-run command at the moment someone needs it, and the Pi guide addendum documents all four flags with a table of the four sensible invocations. The addendum also tells the agent to volunteer that command after an init instead of waiting to be asked, and pre-empts two things that read as failures but are not: the first phase is never steered because there is no closeout to steer from, and a stopped auto run explains itself in its summary block rather than in the phase result.

v0.19.3

Choose a tag to compare

@github-actions github-actions released this 10 Sep 09:09
7bc13b6

Fixed

  • Handoff YAML rejected folded block scalars, blaming indentation. closeout_summary: >- — valid YAML, and the natural choice for a wrapped prose field — failed with Invalid YAML indentation near: <first line of the text>. The parser handled only | and |-, so >- fell through to the plain-scalar path and the block body underneath it then failed the indentation check, producing an error that pointed at whitespace when the actual cause was the scalar style. That sends you re-indenting instead of changing one character. It matters because a handoff is usually written by a model and a model reaching for a wrapped prose field reaches for >-; this was found during the 0.19.2 surface verification by writing a handoff by hand and hitting it immediately. The parser now supports both block styles and all three chomping indicators — |, |-, |+, >, >-, >+ — with real folding semantics: a single line break between content lines becomes a space, a run of k blank lines becomes k newlines, and more-indented lines keep their breaks so a folded block can still hold an indented snippet. Literal blocks parse exactly as before; |+ is newly supported alongside them. The shipped templates/phase-handoff.yaml note, which said only literal blocks were supported, now names both.

  • /codecarto-guide left the model stalled instead of informed. It read the guide, tried to obey it, found no codecarto_* tools, concluded the MCP server was unavailable, and asked which repository and which pipeline to use rather than doing anything. Two causes, both in the Pi wrapper. The guide arrived as a bare user message — the handler queued the document through pi.sendUserMessage with no preamble, so ~200 lines of imperative instructions landed as if the user had typed them, with no task attached; a model given instructions and no task either starts driving immediately or stalls asking what to do, and the reported session did both in turn. And the guide is written for MCP: it says to call codecarto_status, codecarto_next, codecarto_validate and codecarto_complete, and that "every tool takes an absolute cwd" — but the Pi extension registers no tools at all, only slash commands the user invokes, so there was nothing to call and their absence read as a broken server. The drive loop differs too, which made following the guide wrong twice over: /codecarto-next executes the phase itself as an isolated sub-agent and then auto-validates and auto-completes it, so the guide's hand-written execute → handoff → validate → complete loop does not describe a Pi session at all. The guide text is unchanged — agent-skill/ stays the single source and core/guide.ts still serves it verbatim to every surface — and the framing lives in the wrapper, where per-surface logic belongs: a reference-not-a-task header, the document embedded whole and unmodified, then a Pi-surface addendum covering the tool-to-command mapping (and the two library tools that have no Pi equivalent), the absent cwd argument, and the auto-validate/auto-complete loop. MCP needed no change; a tool result is framed by being one. Verified live with the same model and the same guide bytes, framing the only variable: before, it committed to the codecarto_* loop and asked for a repo path and pipeline; after, "I've read the CodeCartographer guide — ready when you are", and asked how to run a phase it answers /codecarto-next and explains that validation and completion are automatic here.

v0.19.2

Choose a tag to compare

@github-actions github-actions released this 10 Sep 08:29
9bcb276

Fixed

  • Phase sub-agents died instantly on any model from an extension-registered provider. A phase failed in 38ms with No API key found for <provider>. Use /login to log into a provider…, while the same model worked in the orchestrator session that launched it, and /login could not fix it because the parent's credentials were never the problem. All three child sessions codecarto creates — the phase runner, the next-phase rewriter, and the dashboard narrator — load with noExtensions: true so a globally installed codecarto does not register its commands and tool guards a second time inside its own sub-agent. But providers a different global extension contributes through pi.registerProvider() reach the model runtime by exactly the same path: Pi registers them from the resource loader that loaded the extension. Stripping extensions therefore stripped the provider table too, so the child inherited the parent's selected model object and then had no provider definition to resolve it against. AgentSession.prompt() checks hasConfiguredAuth before its first turn, that check returned false for a provider the child had never heard of, and the phase was dead before a single token was sent — which is why the failure was instant and cost nothing. Built-in providers were unaffected, so this only bit users who had wired up their own. Child sessions now carry the parent's registered provider configs onto their own runtime, keeping the sub-agent on the model the user actually picked while preserving the extension isolation the flag is there for.

  • Reasoning tokens were eating the output budget, truncating lens JSON and billing for thinking that was thrown away. Broad-Side sent no reasoning field at all, so every model applied its own default. A reasoning-capable model then spent its max_tokens thinking rather than answering: one measured run used 5,758 of a 6,000-token budget on reasoning and left ~230 tokens for the JSON, which truncated mid-structure on 11 of 13 slices. Those tokens bill at the full output rate, so the run paid for roughly 6,000 output tokens per slice to receive 230 usable ones — and estimateCost had no way to anticipate it, since it budgets output on the assumption that output means JSON. #133's truncation repair could not rescue it either: doubling the cap scales the reasoning budget too, so 24 completed retry jobs moved that run from 2 parseable slices to 3. This was not a multi-model problem. The shipped default model does the same thing less consistently — reasoning from 0 to 5,757 tokens across the same 13 slices, three of them cut off at finish_reason: length — so runs have been quietly losing slices to it all along. Lens requests now carry an explicit reasoning budget, capped at a quarter of the lens's output allowance so three quarters remain for the answer, which is precisely the split estimateCost already assumed. It is a cap rather than an off switch on purpose: google/gemini-3.8-flash:batch refuses an entire batch with "Reasoning is mandatory for this endpoint and cannot be disabled", so disabling turns a partial result into none at all — verified live, 13 of 13 requests failed that way. Capping works either way. Measured on the same model, same commit, same 13 slices: 2 of 13 slices parseable before, 13 of 13 after, with finish_reason: stop and reasoning_tokens: 0 across the board, and 37 findings where the uncapped run produced 6. A lens can set its own reasoning, and reasoning: in .codecarto/broadside/config.yaml overrides every lens.

  • The shipped config.yaml recommended a model that cannot be submitted. Its lens_models example named anthropic/claude-opus-4.5:batch for the security and defect lenses, and that id has no batch endpoint — submitting it returns Model 'anthropic/claude-opus-4.5:batch' does not have a :batch endpoint. Every Anthropic and OpenAI batch id tried so far is rejected the same way, while Google's and DeepSeek's work, and nothing in OpenRouter's catalog distinguishes them beforehand. The example now names a verified-submittable model, and both config.yaml and the Broad-Side skill explain that the models action lists ids the Batch API will refuse and that a rejected batch costs nothing, so a candidate should be probed on one lens before being relied on.

  • /codecarto-complete threw its refusals instead of showing them. Completion refuses for reasons the framework words carefully — a missing phase handoff names the file to write and every field it needs; a carry-forward without derives_from and a closure lacking runtime evidence explain what is missing and why. That guidance is the entire point of the refusal. Every other failure in this handler was caught and notified, but the autoCompletePhase call was not, so those messages escaped as a rejection and the user never saw them. It was the only unguarded call of its kind in the extension; switch-pipeline, refresh-scaffold, amend and the post-phase path all wrap theirs. The irony was sharp: /codecarto-next catches this same throw and advises running /codecarto-complete manually, which then threw. Found by writing the first tests that invoke these handlers at all.