Commit Graph

113 Commits

Author SHA1 Message Date
Levi Neuwirth efa8f6f8ed M11.3: Decorations projection (selection + diagnostics)
SemanticRenderState now also projects InstanceMessage::Decorations
from the instance-side state pmacs actually has.

- Selection: per-window byte-native state via active_window_for(fid)
  (SemanticRenderState now carries the session FrontendId), gated to
  the declared buffer and clipped to the viewport →
  DecorationKind::Selection.
- Diagnostics: the shared DiagnosticStore keyed by file URI. Made
  lsp::path_to_file_uri pub(crate) (byte-identical to the Lua
  file_uri_for) so the projection reproduces the exact store key from
  core.file_path. LSP (line,col) -> byte via a line-start scan
  against the buffer source; severity -> DiagnosticError/Warning/
  Info/Hint. Clipped to the viewport.
- StyleSpans and Decorations suppress unchanged frames independently
  (separate last_* maps): a selection move doesn't force a styling
  re-send and vice versa.
- Deliberately NOT emitted: SearchMatch/SearchMatchActive (no
  instance search-hit store), CurrentLine (frontend derives from
  CursorByte; emitting it would breach the contract boundary).
- InlineAdornments/BlockAdornments/FoldState remain unproduced by
  design — no inlay/blame/lens/fold/diff source exists in pmacs yet.
  Honest stubs (the M11.1 "declared, not yet wired" discipline), not
  empty messages every frame.

Dispatcher updated for SemanticRenderState::new(frontend_id). Lib
(1394 crdt / 1239 non-crdt) + integration green on both feature
flavors; clippy -D warnings clean on both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 20:47:18 -04:00
Levi Neuwirth 4ba6fd7bb8 M11.2: semantic projection seam (SemanticRenderState)
The first real producer of the semantic-frontend arc. The instance
projects syntax styling to semantic_render sessions without
rasterizing to a cell grid.

- src/semantic_render.rs: SemanticRenderState, sibling of
  instance_render::RenderState. Reads the same EditorState, emits
  InstanceMessage::StyleSpans (tree-sitter spans via the active
  Theme), scoped + clipped to the FrontendEvent::Viewport byte range.
  Emits nothing until a viewport is declared; suppresses
  byte-identical frames (per-span delta encoding deferred to M11.4).
- CrdtState::version_scalar(): oplog version vector summed to a
  monotonic non-decreasing u64 — the StyleSpans.generation anchor.
- daemon dispatcher: semantic_states map parallel to render_states;
  projection selected per session. Semantic sessions get StyleSpans +
  CursorByte + BufferSnapshot + CrdtOp + presence, never CellDelta /
  grid Cursor. FrontendEvent::Viewport consumed (routed by
  authenticated source). SessionEstablished body extracted to
  handle_session_established (clippy 100-line ceiling). Grid-less
  sessions no longer panic the _ => apply_event arm.
- InstanceCapabilities default semantic_render flipped to
  cfg!(feature = "crdt") — the "M11.2 enables semantic" moment,
  analogous to the M10.8 Day-4 flip. M11.1 negotiation test comment
  updated for the flip (frontend-side default still false keeps M10.7
  outcomes unperturbed).

Lib (1392 crdt / 1237 non-crdt) + integration suites green on both
feature flavors; clippy -D warnings clean on both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 20:14:08 -04:00
Levi Neuwirth 0201943c97 T M4.5 frame-loop reorder: tick_async last (1-frame LSP await latency)
The async bridge settles awaiters inside `tick_lsp`/`tick_mcp` by
posting to the message bus; `tick_async` drains that bus and resumes
the parked coroutine. With `tick_async` running *first* (historical
accretion from M3.3, predating processes/LSP/MCP), every LSP/MCP
`:await()` resumption was deferred a full frame: the response
absorbed in frame N's `tick_lsp` wasn't observed until frame N+1's
`tick_async` (~33ms structural floor @ 60Hz, plus a render frame).

Reordering both production loops (`editor::run` and the daemon loop)
to `processes → lsp → mcp → async` makes settle→resume happen in the
same frame, halving the floor to one frame. The only documented
ordering invariant — `tick_processes → tick_lsp → tick_mcp` for
same-batch supervisor I/O — is preserved; settle (bus post) and
resume (bus drain) are bus-decoupled, so the move cannot regress
correctness in either direction.

Acceptance tests open-code their own per-test tick orders and never
drive `editor::run`, so none covered production ordering. Added
`m4_5_await_resolves_same_frame_as_response_absorbed`, which drives
the exact production order and asserts the awaited request resolves
in the same frame its response is absorbed (absorbed_cycle ==
done_cycle); it fails if anyone reverts to `tick_async`-first.

Gate: fmt clean; clippy --all-targets -D warnings clean; lib
1223/0; m4_acceptance 59/0; m9_1 18/0; m8_1/m8_9/m8_10 green
(SP-7 outline-aggregate "one async tick" pin unaffected).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 19:58:04 -04:00
Levi Neuwirth 1c5169b8f8 M11.1: semantic-frontend protocol scaffolding (wire + capability)
First milestone of the M11 semantic-frontend arc
(docs/semantic-frontend-protocol.md). Wire-format scaffolding only —
no producer or consumer; mechanically identical to the M10.5 CRDT
wire declaration, and non-breaking by the same slice-membership +
per-session-filter argument.

- PROTOCOL_VERSION 2 -> 3; SUPPORTED_PROTOCOL_VERSIONS [1,2,3]. v0.1
  and v1.0 binaries keep connecting unchanged (membership, not
  strict equality).
- semantic_render capability bit on FrontendCapabilities,
  InstanceCapabilities, NegotiatedCapabilities (#[serde(default)];
  instance default false until the M11.2 projection seam).
  negotiate_capabilities AND-combines it and enforces the
  semantic_render => crdt_replica dependency as a CapabilityMismatch
  (a semantic session is also a text replica), never a silent
  degrade. PMACS_INSTANCE_SEMANTIC_RENDER env override added.
- InstanceMessage SemanticFrame family: StyleSpans, Decorations,
  InlineAdornments, BlockAdornments, FoldState, ResourceOffer.
  FrontendEvent::Viewport. Supporting types: ByteRange, StyleSpan,
  Decoration/DecorationKind, InlineAdornment/AdornmentPlacement/
  AdornmentContent, BlockAdornment, ResourceBody. All byte-anchored;
  no pixels cross the contract boundary.
- Grid TUI (frontend.rs) and daemon apply_event drop the new family
  silently — the "declared, not yet wired" posture CrdtOp held
  between M10.5 and M10.8. Stale v1.0 version-pin tests updated to
  the v1.1 truth; negotiation matrix + postcard round-trips added.

Lib + integration suites green on both the default and crdt feature
flavors; clippy -D warnings clean on both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 19:39:07 -04:00
Levi Neuwirth d3fa63290a Collapse if-let nests into let-chains (MSRV-1.95 collapsible_if sweep)
Root cause of the CI Lint regression: commit 6113c53 bumped
rust-version 1.85 -> 1.95. clippy::collapsible_if is MSRV-gated —
collapsing `if let { if let }` needs let-chains, stabilized in Rust
1.95. At MSRV 1.85 clippy suppressed these; at 1.95 it emits them.
The patterns were pre-existing; the MSRV bump surfaced 47 of them
and turned `Lint (luajit)` / `Lint (lua54)` red at HEAD (was green
through PR #7; red from PR #8 = the release-prep MSRV bump).

Resolution (operator-chosen: autofix into let-chains): applied
`cargo clippy --fix` across the luajit, lua54, and crdt lanes
(--all-targets). The fix only applied with the lint at warn level;
`-- -D warnings` turns it into an error and blocks --fix.

Verified on the pinned 1.95.0, all three lanes:
clippy --all-targets -D warnings clean (luajit / lua54 / crdt);
fmt 0 diffs; lib tests 1223/0.

Note: the prior #6 "quiescent audit, clippy clean" was inaccurate —
clippy was not actually re-run there (build/version/fmt only), so
this MSRV-gated regression went uncaught until the live attach-debug
investigation surfaced it. This commit restores genuine clippy
cleanliness at MSRV 1.95.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 14:29:36 -04:00
Levi Neuwirth ed78465aa7 CI fixes v4 2026-05-18 13:32:56 -04:00
Levi Neuwirth 146583d32a CI fixes 2026-05-18 12:24:35 -04:00
Levi Neuwirth 7171282b57 Pin toolchain to 1.95.0 + mechanical clippy/rustc fixes
CI was red on every recent main commit (pre-existing, not from the
V0.2/audit work): the workflow installs rolling `stable`, which on
the runners is ~1 year newer than the local toolchain that validated
the code. Under `RUSTFLAGS: -D warnings` + `clippy -- -D warnings`,
new rustc/clippy lints across pre-existing code became hard failures.
Confirmed identical on the 4 commits before v1.0-rc (e.g. the
`rope.rs:1076` unused_parens compile error is byte-identical there).

Resolution:

- `rust-toolchain.toml` pins channel 1.95.0 (the validated version).
  The repo directory override makes every cargo invocation use it
  regardless of what the CI action installs, eliminating the
  local/CI toolchain-drift class permanently. Bump deliberately.
- Mechanical lint fixes (~17 sites, all the trivial/auto-fixable
  class — no logic change): `cargo clippy --fix` + `cargo fix`
  applied the machine-applicable set; hand-fixed the residuals:
  daemon.rs (duplicated #[allow]), completion_framework.rs
  (sort_by -> sort_by_key/Reverse), attach.rs (map().unwrap_or ->
  map_or, crdt), buffer.rs (is_some+expect -> match, crdt),
  m10_11_acceptance.rs (if -> match guard x2, crdt).
- `cargo fmt --all` (clippy --fix left overlay_paint.rs unformatted).

Verified clean under 1.95.0, all lanes: fmt 0 diffs; clippy
--all-targets -D warnings clean for luajit, lua54, AND crdt;
-D warnings build clean luajit+lua54; doc tests pass; lib 1223/0;
autofix-modified tests (m7_5, m8_1 incl. the Finding-2 fs_watch fix)
pass.

Scope: this clears CI red class #1 (toolchain-gap lints) only.
Independent and still triage-pending: #2 macOS F9 nix
PeerCredentials portability (Test (macos-*)), #3 M1/M4/M6 perf/fuzz
gates. Per plan, those are triaged after CI confirms #1 green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 11:38:57 -04:00
Levi Neuwirth fa1ee116bf daemon 2 2026-05-17 19:11:32 -04:00
Levi Neuwirth 8490e79bbb M10.11 fixes 2026-05-15 22:04:42 -04:00
Levi Neuwirth b6c07cb840 M10.11: adversarial two-laptop acceptance + jitter; the M10 arc verified
The M10 acceptance milestone (two-laptop edit). Framing-pass review
reframed it from confirmatory to **adversarial** verification: the
verification-milestone premise check (M10.11's own discipline,
extracted at M10.10 Day-4) caught its own first-draft framing
asserting "the architecture is complete; this is the verification
milestone" — M10.10's initial verdict was wrong and took six
post-audit rounds, so the M10 arc's correctness is not safely
assumable. M10.11 actively tries to break the arc rather than
confirm it.

Implementation (src/daemon.rs, tests/m10_11_acceptance.rs,
tests/m10_11_perf.rs; prior-pass synthesis/PTY-doubled/Drop-guard
fixtures landed in 05fbbd9's tree, completed here):

- Jitter seam: PMACS_INSTANCE_LATENCY_JITTER_MS + _SEED (SplitMix64,
  no-unsafe/no-dep, default 0xC0FFEE). Q6's "no new injection seams"
  preserved — one sleep-site; jitter-mode delays CellDelta|CrdtOp,
  fixed-latency mode stays CellDelta-only so criterion-1 behavior is
  byte-identical. No drops (Tension B: "packet loss" = latency
  variation only).
- Q13 adversarial scenarios: cat-1 (concurrent same-position
  inserts → deterministic peer-id tiebreak, pinned "A1B1"), cat-2
  (per-frontend undo under causally-pending delayed delivery → B's
  no-op undo doesn't reach A's ops; converge "12"), cat-3 narrowed
  (CRDT state converges across reattach via BufferSnapshot, pinned
  "a1b1"; undo-across-reattach deliberately NOT asserted per
  Finding 4).
- Q8 convergence-under-jitter (seed-pinned; delivery-order-
  independent, pinned "aAbB").
- cat-1/cat-2 pass clean — the arc holds under attack at runtime.

Five findings, all pre-embed (framing-time / Day-1 grep / Day-2
implementation), zero post-audit revision rounds (audit/framing/
prereq docs are gitignored internal-only; this message is the sole
version-controlled record):

- F1 (framing-time): verification-milestone premise check caught its
  own reframe — third arc instance of a discipline addition catching
  a contemporaneous failure.
- F2 (Day-1): framing cited stale fixture locations (β
  framing-pass-time incompleteness, not α temporal drift); Q3
  promotion already done by 05fbbd9's DRY refactor.
- F3 (Day-1): adversarial layer empirically absent in prior
  implementation — validates the reframe (everything confirmatory
  existed, nothing adversarial did).
- F4 (Day-1, M5.8-inherited): reconnect issues a fresh FrontendId
  (no handle_reattach), orphaning per-frontend undo across reattach.
  Classified C; v1.0 action B-i (MANUAL-TEST-CHECKLIST Scenario 4
  documents the limitation honestly + workaround) + B-ii
  (V0.2-PREREQUISITES: SO_PEERCRED-min / token-extended paths).
  Fourth end-to-end-exercise case; first extending the pattern
  beyond M10.8 to a second prior milestone (M5.8).
- F5 (Day-2): Q6×Q8 composition miss — jitter target (CellDelta) ≠
  criterion-3 assertion target (CrdtOp); caught pre-embed by the
  composition-consistency discipline; resolved (B). M10.11-internal
  composition miss (M10.10 Finding-2/4 shape), not inherited.

Scorecard (Option C dual): layer (a) 6/8 milestones-not-findings
(M10.11 joins M10.10 via F5's composition cluster) / 1/8
findings-as-failures; layer (c) 6/8 (M5.8 joins M10.8 via F4;
two clusters — CRDT-pipeline {F1,F3,F5a-M10.8}, reconnect-identity
{F4-M5.8}). Dual-value: layer (a) prediction failed on F5;
pause-point value held (caught pre-embed). M10.11's 5-finding
density empirically validates M10.10's predictive-density model —
property-(b)-at-max, no (a)/(c) → moderate, all pre-embed, zero
post-audit rounds. First validation of the model M10.10 produced.

Verification (clean checkout): lib luajit+crdt 1364/1364, luajit
1211/1211; m5_5 crdt 36/36 (criterion-1 byte-preserved through the
latency-site restructure) + non-crdt 15/15; m10_11 CI-default 5/5
(3 PTY-doubled #[ignore]d, operator-invoked pre-tag); clippy 0
both lanes; fmt clean.

The M10 arc is verified. v1.0 ships after M10.12 (release tag +
TRANSITION-M10.md + collaboration user guide, which inherits the
Scenario-4 honest wording).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 20:51:00 -04:00
Levi Neuwirth 45be65b026 M10.10 ship gate
Land the optimistic local-edit-application layer on top of the M10 CRDT
foundation: frontend-side rope replica with local edit application,
daemon-authoritative broadcast, and bidirectional cursor reconciliation.
Keystrokes feel instantaneous because the local replica answers next-render
queries before the daemon round-trip completes, while the daemon remains
the single source of truth for conflict resolution and broadcast to remote
replicas.

Architecture beats:
- BufferMirror (src/buffer_mirror.rs) holds a per-frontend rope replica
  with explicit cursor-staleness tracking. Every event that may move the
  active cursor or swap the active buffer marks the mirror stale; the
  next CursorByte from the daemon clears it.
- CrdtOpOrigin {OptimisticReplica(FrontendId), DaemonKey} routes broadcast.
  OptimisticReplica skips re-application on the originating frontend
  (already applied locally); DaemonKey broadcasts to all replicas including
  source -- covers Lua-driven and generated-buffer edits that bypass the
  optimistic path.
- Generated buffers (*help*, *workers*, *pmacs-instance*, *errors*) funnel
  apply_edit output through queue_daemon_origin_crdt_op so post-attach
  CRDT upgrades don't drop their edits.
- forbid(unsafe_code) preserved throughout; loro 1.12 added as the CRDT
  engine.

Audit posture: M10.10 shipped through six post-audit review rounds with
twenty-eight cumulative findings, most categorized as "incomplete
application of a prior round's mechanism." The audit doc records
grep-driven exhaustiveness as the standing countermeasure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:28:46 -04:00
Levi Neuwirth 4da4b09d5d Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00