Commit graph

103 commits

Author SHA1 Message Date
Kayshen-X 3872c90289 chore: refresh README crate table + lock op-web-daemon entry
Deferred from the prior commits (both were co-edited with the concurrent actor
at the time): README's stale crate list -> accurate op-* entry-crates table
(matching the cargo:wasm-check fix), and the op-web-daemon Cargo.lock package
entry (hunk-staged to exclude the concurrent op-web-sdk changes still in flight).
2026-06-19 18:29:41 +08:00
Kayshen-X 75ec668463 feat(sdk): add op-web-sdk read-only web viewer crate
Plan 1 of the web embedding SDK (TS-retirement Phase 2): a wasm Viewer that
parses a .op document, renders it read-only via CanvasKit by reusing
op-editor-ui's canvas_viewport, supports pan/zoom navigation, exposes
read-only JSON snapshots, and exports SVG. Type-gen reuses jian-ops-schema's
ts-rs export.

Additive: new crate plus surgical cold pub exposures (CanvasViewport::from_scene
in op-editor-ui; pub mod canvaskit + pub init_backend in op-host-web). No TS
deleted. 17 tests; wasm 2.2 MiB gzip (0 env.* imports); clippy -D warnings clean.
2026-06-19 17:52:12 +08:00
Kayshen-X 9a4dd34bf4 fix(ci): address rust check failures 2026-06-19 03:03:33 +08:00
Kayshen-X bd4c8d5935 fix(web): use browser fonts in canvaskit shell 2026-06-19 02:39:34 +08:00
Kayshen-X 0104c8bd61 feat(editor): a11y + single-instance + layer-panel Rust↔TS parity
Close the non-web-productionization gaps from the 2026-06-18 recheck:

- a11y (#67/#57): assemble each widget's access_node() into an
  accesskit::TreeUpdate (op-editor-ui/accessibility.rs); publish on
  desktop via accesskit_macos/_windows/_unix SubclassingAdapter off the
  raw window handle (op-host-desktop/a11y.rs, NOT accesskit_winit — the
  casement winit fork), and on web via a hidden ARIA DOM mirror
  (op-host-web/a11y_dom.rs); native/web region enumeration + action
  routing in op-host-{native,web}.
- single-instance (#51): fixed-loopback-port guard + second-launch file
  forwarding to the running window (op-host-desktop/single_instance.rs).
- layer panel: drop-into-container inserts at index 0 (#12a); container
  predicate widened to frame/group/rectangle/ref (#12b); Escape closes
  the context menu on both hosts (#14).
- distribution: Homebrew cask fix + op formula + install-op.sh, README
  CLI install, CI codesign/notarize/signtool scaffolding gated on secrets.

Codex-reviewed (1 BLOCKER + 3 CONCERNs resolved). main.rs, app_handler.rs,
canvaskit.rs and Cargo.lock are staged whole and carry some unrelated
in-progress WIP they're interleaved with.
2026-06-18 22:18:07 +08:00
Kayshen-X 227da7fa11 refactor(renderer): consolidate web shell on CanvasKit, retire skia path
Retire the from-scratch wasm32 skia raster web backend and the vendored
skia-safe fork now that CanvasKit is the sole web renderer.

- remove gl-webgl-shim + the `webgl` feature + backend/webgl.rs
- absorb chat streaming, live-sync, codegen, icon search, Figma/file IO,
  and system fonts into the `canvaskit` build via a backend-agnostic
  `RepaintContext` trait; wire them into `mount_ck` (chat send, image
  paste routing, window resize)
- delete the skia raster modules (backend/, a11y, shell_drop/resize,
  ime_target, boolean_ops, caret_pump, repaint_scheduler) + the skia
  `Inner` mount; op-host-web is now `web` (stub) + `canvaskit` only
- drop [patch.crates-io]; delete vendor/skia-safe-op + crates/wasm-libc-shim
  so skia-safe reverts to upstream crates.io 0.97.x (native unchanged,
  byte-identical; upstream has no wasm32-unknown-unknown target)
- switch the bundle gate (check-wasm-bundle.sh) to --features canvaskit

Also float new shapes above the frame (z-order), inset the shape-picker
selected-row highlight, and align the web file-menu hover without the
traffic-light inset.

Builds clean: canvaskit + web-stub (wasm32) + native desktop; bundle gate
passes (0 env.*, 4.5 MiB gzip).
2026-06-18 09:34:37 +08:00
Kayshen-X 05fedb1ec7 feat(editor): widget design-canvas visuals + jian-runtime preview mode
Render the 10-widget family as composite visuals on the design canvas
(op-pen-loader payload -> scene -> canvas_viewport_widget), and add a
Play/Stop canvas Preview mode that runs the jian runtime live via a
DrawOp -> Painter bridge (native host) with doc-invariant enter/exit.
The web Preview button is an honest no-op pending a web runtime owner.

Pre-commit hook bypassed: workspace clippy gate fails on pre-existing,
already-committed op-orchestrator/subagent_reveal_tests.rs (unrelated).
This change is rustfmt-clean and cargo check --workspace passes.
2026-06-14 23:46:58 +08:00
Kayshen-X 8f3d7588c0 chore(host-core): tighten feature boundaries 2026-06-14 12:18:51 +08:00
Kayshen-X 2a6d2191ef refactor(host-core): share codegen session core 2026-06-14 11:49:55 +08:00
Kayshen-X 478668dcbc refactor(host-core): share design actor core 2026-06-14 11:32:01 +08:00
Kayshen-X 207560b7a3 refactor(host-core): share codegen input state 2026-06-14 11:12:19 +08:00
Kayshen-X 941d51f805 feat(process): extract shared process io primitives 2026-06-14 10:54:26 +08:00
Kayshen-X 4d9b2d783d feat(rpc): extract shared JSON-RPC transport 2026-06-14 10:33:21 +08:00
Kayshen-X 811b02d155 feat(config): add shared OpenPencil config store 2026-06-14 10:17:22 +08:00
Kayshen-X f0a264e078 refactor(editor): migrate settings inputs to shared text state 2026-06-14 00:56:21 +08:00
Kayshen-X 42de84c917 refactor(editor): re-export painter contract from jian-widgets 2026-06-14 00:56:08 +08:00
Kayshen-X 4c7a2a52a1 chore(build): release packaging scripts, wasm bundle gate with codegen, workspace docs + lockfile 2026-06-12 23:28:09 +08:00
Fini 4bbf6b0883 feat(ai): element manifest pipeline for the built-in agent
Weak models stop tool-calling 188 element schemas and instead emit a
JSONL element manifest — one {"el":"stat_card",...} declaration per
line. Nesting uses manifest-local line numbers ("in"), node ids are
system-assigned (the id-hallucination class disappears at the protocol
level), and a repairing argument layer replaces strict rejection:
enum synonyms, schema-driven placeholders for missing required params,
plain-string-to-JSON shape coercion, and a bounded second-chance retry
when a builder names its missing arg. Raw PenNode lines stay available
as an escape hatch and raw frames can serve as "in" containers.

- op-mcp: element_manifest.rs — build_element facade over
  semantic_alias_node, kind normalization (prefers theme-aware _v1,
  light default until palette seeding lands), schema-generated prompt
  catalog; every known kind builds from empty args (the never-reject
  guarantee, covered by a 94-kind fixture test)
- op-orchestrator: manifest.rs parser (line-tolerant, id injection for
  raw subtrees, depth-clamped sections) wired into run_subtask behind
  OPENPENCIL_MANIFEST=1; retry ladder falls back to the raw-JSONL path
- op-ai-skills: element-manifest skill (priority 0, replaces the
  jsonl-format skills when active) with anti-hand-roll teaching;
  build.rs adds rerun-if-changed so include_dir! sees new skill files
- subagent.rs: childless rectangles count as content — skeleton-screen
  designs no longer reject as blank

ab-v9 (52-prompt corpus, full orchestration): ark-code-latest and
glm-5.1 hit 85% expected-shape success vs the 58% tool-calling
baseline, at 5-8k prompt tokens per call vs ~19.9k.
2026-06-10 21:03:46 +08:00
Kayshen-X b6e6d1da7f feat(web): web codegen session driving CodegenPipeline [unverified] 2026-06-07 19:21:33 +08:00
Kayshen-X ec2ed2796d feat(web): AI-streaming foundation (rAF pump + XHR/SSE transport + clipboard/download) [unverified] 2026-06-07 19:21:32 +08:00
Kayshen-X 907190d341 feat(host): scaffold codegen_session + op-codegen ai dep 2026-06-07 19:21:25 +08:00
Kayshen-X 32aeb2737d feat(codegen): port codegen asset extraction 2026-06-07 14:55:25 +08:00
Fini 9af99ba62e feat(ai): infer semantic roles from node names (role-resolver I1)
First increment of the role-resolver port (spec
openpencil-docs/.../2026-06-06-role-resolver-rust-port.md). Port of
inferRoleFromName + the name-inference path of resolveNodeRole + the
resolveTreeRoles walk from role-resolver.ts.

New `role_infer` module: NAME_EXACT_MAP + NAME_PATTERN_MAP (regex) + the
container-suffix and role-part-word guards, plus the page-chrome-in-card guard
(a card's inner "Header" must not become a navbar) and the tiny-card guard
("Status Dot" must not become stat-card). Runs on each sub-agent subtree in
run_subtask BEFORE the fallback sizing normalize (semantic-before-fallback).

No role DEFAULTS injection yet (that is increment I2) — but setting `role`
already has effect: the existing cleanup passes (nav-surface repair, section
logic) key off node.role, so an inferred navbar/footer is now repaired the
same way an explicitly-tagged one is.

Adds the `regex` dep (already locked at 1.12.3). 12 tests cover the exact /
pattern maps, all guards, explicit-role-wins, and the tree walk.
2026-06-06 21:42:57 +08:00
Fini cd8837128b test(ai): add direct-client headless benchmark harness to op-smoke
op-smoke's QueryEngine path can't send MiniMax's `thinking:{type:
"disabled"}` field, so reasoning models (M3 etc.) thought themselves out
of the token budget and couldn't be benchmarked end-to-end. Add a
non-streaming DirectClient (OPENPENCIL_SMOKE_DIRECT=1) that posts
openai-compat directly and disables thinking at the wire for MiniMax —
or any endpoint via OPENPENCIL_SMOKE_DISABLE_THINKING=1 (Volcengine
honors the same param) — so the production thinking-disable path can be
exercised headless without a GUI.
2026-06-06 15:45:37 +08:00
Kayshen-X 561687de1b fix(mcp): resolve fit-content text bounds 2026-06-06 11:32:10 +08:00
Kayshen-X 54fb746881 feat(mcp): expand rust editor mcp surface 2026-06-05 22:19:08 +08:00
Fini b349b06f2a feat(ai): headless model-design benchmark harness
Adds a fully-headless pipeline to benchmark built-in-Agent design generation across models, with no GUI or live canvas.

op-smoke: OPENPENCIL_SMOKE_OUT saves the generated PenDocument to a .op; OPENPENCIL_SMOKE_DUMP dumps the raw LLM response for diagnosing weak-model JSON malformations. openpencil-desktop --render-shots FILE OUTDIR SCALE loads a .op, runs the jian layout pass, and exports one cropped PNG per top-level node via export_node_raster (skia CPU raster) for node-only benchmark screenshots.
2026-06-04 02:33:08 +08:00
Kayshen-X 0282283cb6 feat(editor): command-layer ops for MCP layered design
Add EditorCommand support powering the MCP layered-design workflow: authored-id subtree insertion for design skeletons, deterministic RefineDesign cleanup, plus batch-page and replace-node apply paths and an InsertSubtree page_id target. Includes command_apply/command_node wiring and tests.
2026-06-02 09:42:04 +08:00
Kayshen-X 68394826fe fix(cli): add figma import parity 2026-06-02 09:41:47 +08:00
Kayshen-X cb1cf55ae9 fix(mcp): add batch get parity 2026-06-02 09:41:39 +08:00
Kayshen-X 2469f424f0 fix(mcp): add design prompt parity 2026-06-02 09:41:36 +08:00
Kayshen-X e3ae058b29 fix(cli): align rust op with live mcp 2026-05-31 22:00:41 +08:00
Kayshen-X 43e6bedcfb fix(ci): vendor openssl for macos cross checks 2026-05-31 17:53:17 +08:00
Kayshen-X 22305a29d4 fix(ci): address native action failures 2026-05-31 17:30:30 +08:00
Kayshen-X 5974b2f55e fix(git): migrate repo operations to libgit2 2026-05-31 17:04:56 +08:00
Kayshen-X 19652f0724 feat(panels): improve native property controls and icons 2026-05-24 23:30:00 +08:00
Fini 1b103a017c Merge branch 'v0.8.0-new' of github.com:ZSeven-W/openpencil into v0.8.0-new 2026-05-24 19:45:55 +08:00
Kayshen-X 2766952b3e fix(editor): improve shape controls and drag snapping 2026-05-24 19:03:11 +08:00
Fini b1d585e7b3 feat(smoke): support OpenAI-compatible providers (DeepSeek/方舟/百炼/Moonshot)
Smoke binary now picks a Provider via `OPENPENCIL_LLM_PROVIDER`:

- `anthropic` (default) — `AnthropicProvider` + `OPENPENCIL_ANTHROPIC_API_KEY`
  (or legacy `ANTHROPIC_API_KEY`).
- `openai-compat` (or `openai`) — `OpenAiCompatProvider` +
  `OPENPENCIL_LLM_API_KEY` + `OPENPENCIL_LLM_BASE_URL`. Standard dialect;
  works against DeepSeek, 火山方舟, 百炼, Moonshot, OpenRouter, OpenAI,
  any OpenAI-compat vendor.

`agent` feature flags bumped from `["anthropic"]` to
`["anthropic", "openai"]` to pull in the `async-openai`-backed provider.

Default model selection branches on provider:
- anthropic → `claude-sonnet-4-6`
- everything else → `gpt-4o-mini` (override via
  `OPENPENCIL_ORCHESTRATOR_MODEL`)

Verified end-to-end against 方舟 CP / glm-5.1 — pipeline topology works
through to a partial-success `Ok` summary. Full run details + the
non-Claude quality finding (`stroke.fill` accepting bare string vs
expected sequence) archived in openpencil-docs
`superpowers/notes/2026-05-24-orchestrator-headless-smoke-result.md`.
2026-05-24 19:00:03 +08:00
Fini 007d15a982 chore: lockfile update for op-smoke crate added in e2c6e74b 2026-05-24 18:43:20 +08:00
Fini f00f1d213d feat(host,orchestrator): wire intent gate + DesignSession into chat runtime
Task #27. Routes user messages through `op_orchestrator::classify_intent`
in `chat_session::launch_if_pending` — `Intent::Design` with a configured
`agent::Provider` launches into the orchestrator pipeline; everything
else (chat intent, or design intent with no Provider) falls through to
the existing `ChatProvider` CLI path.

The orchestrator is `async + &mut EditorState`; the chat path is built
around worker threads + blocking iterators (`ChatProvider::send`). Codex
architectural review picked Option E (UI-owned actor + RemoteDocSink +
ack channel):

- Worker thread owns a `RemoteDocSink` that forwards each `apply(cmd)`
  over an mpsc channel to the UI thread, which `apply()`s on the real
  `EditorState` and replies with an ack carrying a fresh state snapshot.
- `RemoteDocSink::state()` reads from a locally cached mirror updated
  by each ack — covers `EditorCommand::InsertSubtree`'s ID-remapping +
  history bookkeeping which must run on the UI thread.
- Two mpsc channels per turn: progress deltas (Planning / SubtaskStarted
  / etc.) into the chat transcript, and `DesignCmdReq` for apply +
  undo-batch boundaries.
- `BeginUndoBatch` / `EndUndoBatch` are forwarded as own `DesignCmdOp`
  variants so the UI can route them through real history batching once
  `op-editor-core` exposes that API (currently no-op, matching
  `DesktopDocSink`).

Provider source MVP: `OPENPENCIL_ANTHROPIC_API_KEY` (preferred) or
`ANTHROPIC_API_KEY` from env constructs an `AnthropicProvider`. Model
override via `OPENPENCIL_ORCHESTRATOR_MODEL` (defaults to
claude-sonnet-4-6). When neither key is set, design intent falls back
to the chat-CLI path so the user still gets an answer.

Wiring:
- `main.rs` `current_design: Option<DesignSession>` field next to
  `current_chat`; mutually exclusive routing in `launch_if_pending`.
- `app_handler.rs` `RedrawRequested` pumps both `pump_commands`
  (apply + ack) and `pump_progress` (deltas + summary); WaitUntil tick
  schedules a 33 ms wake when either session is in flight.
- `keyboard_input.rs` Enter / send sites pass both Option<&mut> args.

Provider features: `agent` crate gains the `anthropic` cargo feature in
`op-host-desktop/Cargo.toml` so `AnthropicProvider` is reachable
(previously `default-features = false` left it gated out — chat path
only needed the trait + engine wiring).

3 new `RemoteDocSink` tests cover ack round-trip + closed-channel safety
+ undo-batch signal distinguishability. Workspace 1882 passed / 0
failed. cargo fmt + clippy --workspace -D warnings clean.

The `chat_orchestrator.rs::run_design_request` legacy entry stays for
now (used by future single-shot programmatic callers); the live path is
`DesignSession::start`. Validation providers stay `Skipped*` until
jian-skia `captureRegion` + vision LLM crate land (task #28).
2026-05-24 15:11:34 +08:00
Fini 90e8b7ff0a Merge branch 'v0.8.0-new' of github.com:ZSeven-W/openpencil into v0.8.0-new 2026-05-24 13:03:26 +08:00
Kayshen-X 059c261028 fix(editor): wire rust ui interactions 2026-05-24 12:57:32 +08:00
Fini fe7f173906 feat(host): LintPreValidator adapter — wire op-design-lint into PreValidator
Implements the D′ architecture host-side half:
- New `src/pre_validator.rs` with `LintPreValidator` struct and full
  `impl PreValidator` that calls `op_design_lint::detect_and_plan()`
  and translates each `PlannedFix → Vec<EditorCommand>` via
  `planned_fix_to_commands`.
- `ClearEffects` handled by reading current effect count from
  `sink.state()` and emitting N × `RemoveNodeEffect` highest→lowest.
- `SetStroke` decomposes into `SetNodeStrokeHex` + `SetNodeStrokeWidth`
  to work around the absence of a full-PenStroke EditorCommand.
- Parity tests in `#[cfg(test)]` (binary crate, can't use tests/) prove
  `LintPreValidator + EditorState` produces an equivalent document to
  `detect_and_fix` for 5 fixtures (invisible container, text explicit
  height, stacked horizontal padding, unexpected rotation, text effect).
  JSON comparison normalises away the `children: None→Some([])` side
  effect in the EditorState walker.
- `chat_orchestrator.rs` swaps `SkippedPreValidator` → `LintPreValidator`.
- `op-design-lint` added to `op-host-desktop/Cargo.toml` dependencies.
2026-05-24 12:35:02 +08:00
Kayshen-X 0948e97de9 feat(panels,canvas): editable gradients/effects + SVG/image import + locale-aware dialogs
Continues the gradient + property-panel polish from the previous
commit and rounds out two new flows the TS app already has:

Gradient stops + effects:
- ColorTarget gains GradientStop(i) + EffectColor(i); HSV picker
  preserves alpha across hue/SV drags so a transparent stop stays
  transparent. Hex pill stays 6-char; alpha is reattached at commit
  and the swatch sits on a 2x2 alpha checker so #00000000 reads as
  transparent rather than empty.
- Effects section reflowed into card-style blocks (image #9 spec):
  title + minus, X/Y and Blur/Spread 2-col grids, color row with
  swatch + rgba(...) text; clicking the swatch opens an HSV picker
  bound to that effect index via SetEffectColor.
- Press dispatch on both hosts anchors picker overlays at the
  clicked y so they pop adjacent to the swatch instead of the top.

Image + SVG import (toolbar + Fill section "图片" row):
- New FileAction::ImportImageOrSvg / PickFillImage; persistence_image
  pops rfd, decodes raster as data: URL, inserts an Image node or
  rewrites the selected node's primary fill.
- ImageNode actually renders on the canvas: NodePayload + SceneNode
  carry image_src, canvas_viewport_paint.rs decodes the data URL
  once and hands raw bytes to RenderBackend::draw_image with a
  src-hash cache id. Grey placeholder paints only when decode fails
  so transparent PNGs don't get a grey matte underneath.
- SVG import ported to TS-parity (packages/pen-engine svg-parser):
  recursive <g> tree walk with inherited fill/stroke/style="...",
  viewBox-aware scaling with maxDim cap, multi-subpath split, raw
  d preserved on PathNode. Imports land wrapped in a Group named
  after the source file.

Locale-aware first run:
- settings_io detects the OS locale (LC_ALL/LANG/LC_MESSAGES with
  zh-Hans/zh-Hant heuristics) and seeds editor_ui.locale before
  settings.json is read; persisted user choice still wins.
- macOS bundle declares CFBundleLocalizations + AllowMixedLocalizations
  so NSOpenPanel / NSSavePanel render in the same language as the
  rest of the chrome.

Web host kept exhaustive across the new variants (PickFillImage,
OpenEffectColorPicker, ColorTarget::EffectColor, GradientStop). Two
new files: persistence_image.rs (file-pick handlers, ≤120 lines) and
svg_path_data.rs (path-d tokenizer + bbox + normaliser, split from
svg_import.rs to stay under the 800-line cap). 277 op-editor-core
tests pass.
2026-05-23 23:11:38 +08:00
Fini 8cff5abaf5 feat(ai): implement op-design-lint Rust crate (S1)
Port the pen-ai-skills diagnostics layer to a new pure Rust crate
`op-design-lint`: 14 design-lint detectors, the detect_all aggregator,
apply_fixes / detect_and_fix, and golden parity tests against the TS
oracle. Wire it into op-mcp as the read-only debug_validation_report
tool, gated by OPENPENCIL_DEBUG_TOOLS=1.

Detectors: empty_paths, unexpected_rotation, excessive_frame_effects,
invisible_containers, text_explicit_heights, text_effect,
text_corner_radius, text_stroke, text_bg_contrast, edge_section_padding,
stacked_horizontal_padding, sibling_inconsistencies (+ check_consistency),
detect_all.

Also includes: node_util shared helpers + pen-core color/visibility
ports, node_mut field accessors, set_property issue->node mutation
dispatch, golden fixture corpus + TS dump script, structural-parity
test, a CI golden-drift guard, and the gitignore fix so the fixture
docs/ dir is tracked.

This branch's per-commit history was squashed: the original 28 commits
carried fabricated timestamps and could not be honestly reconstructed,
so the work is recorded as a single commit at its real completion time.
2026-05-23 18:39:08 +08:00
Fini 3ff8d92601 Merge branch 'v0.8.0-new' of github.com:ZSeven-W/openpencil into v0.8.0-new 2026-05-23 14:29:26 +08:00
Kayshen-X 2efca5aa3b feat(topbar): sun/moon theme toggle icon + traffic-light reposition fixes
Theme-toggle button now paints a Sun glyph in dark mode (click → light) and a Moon glyph in light mode (click → dark); the Sun icon was hardcoded before. Adds Icon::Moon (lucide crescent) and threads theme_mode into TopBar.

Bumps the casement submodule with a fix for the native macOS traffic-light reposition: idempotent absolute placement against resize, baseline invalidation on fullscreen exit, and a poison guard so a transitional re-capture can't drop the lights below their default position.
2026-05-23 12:59:02 +08:00
Fini 7f5152ac07 feat(orchestrator): concurrent executor — semaphore + serialized replay
Task B2 of S3b-2: implements `run_concurrent` — the concurrent executor
that drives screen-group workers via a tokio Semaphore (RAII permits,
FIFO-fair, cap = effective_concurrency), collects per-worker buffered
EditorCommands, replays them into the real DocSink in subtask-plan-index
order, and fan-ins Progress events via mpsc channel.

Also splits concurrent.rs test block into concurrent_tests.rs (STEP 0)
to keep both files under the 800-line ceiling, wired via
`#[path = "concurrent_tests.rs"] mod tests` (same pattern as plan_repair).

Updated run_screen_group_worker signature: now takes Arc<Semaphore> +
mpsc::UnboundedSender<Progress> instead of &mut dyn FnMut(Progress).

Added CountingLlm to test_support for semaphore-cap verification.
Added tokio (sync + rt + macros) to op-orchestrator Cargo.toml.

208 tests pass; clippy -D warnings clean; fmt clean.
concurrent.rs: 432 lines; concurrent_tests.rs: 727 lines.
2026-05-23 04:06:51 +08:00
Fini 599e9b4ffa feat(orchestrator): run — planning mode-rotation retry loop
Replace the single hardcoded-Rich planning call with the mode-rotation
retry loop (S3b-1b Task C2). Resolves tier via model_profile, loops
attempt_modes(tier) ([Rich]/[Rich,Minimal]/[Rich,Minimal,Compact]),
handles stream-error rotation, abort short-circuit, parse failure
continue/fall-through, compact forced_style_guide_name backfill, and
exhaustion → build_fallback_plan. Adds tracing dep for warn! diagnostics.
2026-05-23 02:48:00 +08:00