PR1 of ck-typeset plan: scaffold the layout-engine crate #56
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/056-pr1-of-ck-typeset-plan-scaffold-the-layout-eng"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
PR1 of ck-typeset plan: scaffold the layout-engine crate
Along the way:
Touches 118 files:
CLAUDE.md,Cargo.lock,Cargo.toml,ck-editor/Cargo.toml,ck-editor/src/view/input.rs,ck-editor/src/view/mod.rsand 112 more.Approving. I would have split
ckeditor/tests/scenarios/move_file_via_tree_alt_j.rsdifferently, but not enough to hold this up.@ -0,0 +65,4 @@h.assert_disk_contents(FILE_IN_ZZZ, KEEP_CONTENT);// Tree: aaa/ now empty; zzz/ now contains drift.txt and keep.md.let tree = ck_app::file_tree(h.app()).expect("file_tree is Some");The expect message on
treeis the only thing a user sees if this fires. Does it say what they should do, or only what we assumed?, at ckeditor/tests/scenarios/move_file_via_tree_alt_j.rs:68.It said what I assumed. Reworded in
ckeditor/tests/scenarios/move_file_via_tree_alt_j.rsso it names the input that broke it, which is what the bug report will need.Deletes ck-editor's hand-rolled pulldown-cmark walker and points the markdown reading view (Esc on .md → md_reading) at the same ck_markdown::Markdown widget that notes_panel already uses. This is the "delete the second old renderer and consolidate" step (formerly PR 7 in the typeset plan), pulled forward as a tidy interstitial before PR 4 lands images — once both surfaces share one renderer, every later typeset PR (images, syntect code blocks, math) lights up both reading view and notes preview for free. ck-editor drops its direct pulldown-cmark dep entirely; it now reaches markdown only through ck-markdown. EditorTheme sheds five typography fields (md_body_size, md_h{1,2,3}_size, md_paragraph_spacing) — those live in ck_typeset::PageConfig now; only the colour tokens remain to seed a ck_markdown::Palette. Net diff: ~-520 lines and one fewer crate-level dep.Dashboard `a` key on a selected row spawns claude inside that Context's worktree (distinct from `n`, which forks a fresh worktree off `root_branch`). New `DashboardAction::SpawnAgent(ContextId)` → `ProjectCommand::SpawnAgentInContext { id }` funnels through the existing `spawn_agent_in_worktree` path so MCP setup, registry registration, and the §6 ownership invariant stay shared. Inline `CloseActiveContext` in `execute_project_command` and split the remaining variants into `try_dispatch_agent_command` / `dispatch_lifecycle_command` to keep `project_command::CkEditorApp` under its CC budget (17 → 6). MCP scenarios were chronically flaky because `McpBus::spawn` read `$XDG_RUNTIME_DIR` and five tests mutated it via `unsafe std::env::set_var(...)`. The "tests run sequentially per binary" SAFETY claim was false — cargo runs scenarios in parallel, so concurrent set_var/var_os races let one test capture another's tempdir, and the original tempdir got dropped while the listener was binding under it. `McpBus::spawn` and `ensure_bus_at` now take `runtime_dir: PathBuf` explicitly; `runtime_dir_from_env` is the single seam used by the production `ensure_bus`. Tests pass tempdir directly, no env mutation. Verified 75/75 green across 50 default runs + 25 runs at --test-threads=8.ck-files grows three primitives so source-tree mutations route through the same seam as content I/O: rename (no-clobber, mirroring create_empty's "do not destroy user work" discipline), remove (single entry that dispatches on inode type — file unlink vs recursive remove_dir_all), and create_dir_all (idempotent mkdir-p). Doctests plus 15 integration tests in tree_mutation.rs cover the happy paths, the no-clobber semantics, missing-source / missing-parent error arms, symlink no-follow, and read-only-parent permission tests for both remove arms. CK018 widens to also forbid std::fs::{rename, remove_file, remove_dir_all, create_dir_all, create_dir} on source paths and picks up CK019's #[cfg(test)] / #[test] exemption shape so unit tests stay readable. Three files added to direct_source_fs_allowed_files: main.rs (log directory bootstrap), logging.rs (log-file rotation), mcp_host.rs ($XDG_RUNTIME_DIR socket lifecycle) — each with a one-line justification per CLAUDE.md. Seven new rule unit tests cover each newly-forbidden op and the test exemption. tree_command's rename / delete / mkdir / move flows route through the new primitives. tree_delete collapses the if path.is_dir() { remove_dir_all } else { remove_file } branch into a single files::remove call; tree_rename and tree_move_to fold their new_path.exists() precheck into a match on FileError::AlreadyExists. Net effect: tree_command::CkEditorApp metrics tighten — CC 34 → 33, RFC 24 → 23 — locked into baseline.toml in the same commit per CLAUDE.md's "ratchets ratchet" rule. baseline_CHANGELOG.md gains a dated entry explaining the ratchet and the rule widening. panels::file_tree_nav::copy_dir_recursive migrates the create_dir_all side; std::fs::read_dir / copy stay direct because ck-files has no counterpart yet. Region coverage on ck-files lib.rs is 99.63% (up from the prior 99.61% baseline noted in README). COVERAGE_EXEMPT.md line refs updated for the shifted write_atomic / sync_all error arms.Production crash on startup: `FontFamily::Name("lm-roman-bold") is not bound to any fonts` from inside `EguiMeasurer::text_width` on frame 1. Reproduced consistently when session restore opened a markdown buffer that paints on the very first eframe frame. Root cause: `egui::Context::set_fonts` queues new font definitions and applies them at the *next* `begin_frame`. The previous font install ran inside `update_frame`, which runs *during* a frame — so frame 1's panel render saw default fonts, panicked on the `lm-roman-*` lookup that ck-typeset's layout pass needs for bold and italic body text. Fix: lift the font install into a public free function `app_layout::pre_install_fonts(&mut CkEditorApp, &Context)` and call it from the eframe creation callback in `main.rs`, before any frame begins. The queued definitions land at frame 1's `begin_frame` instead of frame 2's, and the in-frame fallback in `update_frame` is gated behind `app.layout.fonts_installed` which `pre_install_fonts` flips on. The harness mirrors the same lifecycle in `Harness::with_files` so scenarios exercise the same boot path eframe drives. Tests: two integration tests in `tests/pre_install_fonts.rs` — the positive test confirms LM Roman families are bound on frame 1 after `pre_install_fonts`; the negative test confirms they are *not* bound without it (so the positive test cannot pass by accident on a context that ships LM Roman by default). A scenario at `tests/scenarios/markdown_buffer_renders_on_first_frame.rs` opens a markdown buffer through the harness and confirms the end-to-end production lifecycle works.When `cargo test` runs from `.git/hooks/pre-commit`, the parent `git commit` exports GIT_DIR, GIT_INDEX_FILE, GIT_AUTHOR_*, GIT_COMMITTER_*, GIT_PREFIX into every child. GIT_DIR overrides Command::current_dir, so every scenario git call — both test-side (run_git, Command::new("git")) and production-side (the app's own git CLI through GitCommand::Push / Commit / etc.) — operates on the host CkEditor repo instead of the scenario's temp dir. That produced three failure shapes from the ticket: host hooks firing inside temp repos, "remote origin already exists", and HEAD-never-advances. Fix scrubs every GIT_* env var in `Harness::with_files` under the existing XDG lock. Verified by reproducing all 9 ticket scenarios with GIT_DIR=…/.git GIT_AUTHOR_*=… cargo test scenarios::git_* before the patch (9/9 fail) and after (9/9 pass).