Skip to content

mac: consume display-list-v2-diagnostics — v2 suggestions reach Problems and Tab-to-fix (after #358) - #363

Open
d-q222 wants to merge 37 commits into
mainfrom
agent/daniel-parent/mac-v2-diagnostics
Open

d-q222 wants to merge 37 commits into
mainfrom
agent/daniel-parent/mac-v2-diagnostics

Conversation

@d-q222

@d-q222 d-q222 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

daniel-parent (Cursor Grok lane, reviewed by daniel-parent plus an independent read-only Codex review). This is the Mac consumer of #358's display-list-v2-diagnostics capability. Merge after #358: this branch contains #358's producer files, so the diff shrinks to the Mac and runtime side once #358 lands.

What changes

  • document-runtime allowlist: it accepts display-list-v2-diagnostics only together with display-list-v2. It still rejects the capability alone, and unknown capabilities, with tests.
  • Request: PreviewV2View asks for the capability alongside display-list-v2. When the producer declines it (including render-pipeline: opt-in display-list-v2-diagnostics capability carries suggestion on the v2 wire #358's whole-family decline on reply overflow), the preview logs that and carries on.
  • Decoding: RenderingV2 and RenderingV2Fast both decode an optional suggestion; "" becomes nil in both readers, and unknown keys are still skipped.
  • Problems panel and editor marks (ShellModel.producerDiagnostics):
    • Rows come from runtime-v1 compile_result.diagnostics when that list is non-empty.
    • Otherwise the live v2 frame's diagnostics are mapped through asRuntimeV1.
    • The two sources are never concatenated, so rows aren't duplicated.
    • v2-only suggestions reach the existing Problems navigation, EditorDiagnostics and mac editor: Tab accepts the fix at the caret (#277) #445's ShellModel.caretFix, so Tab accepts them. There's no second accept path.
  • Delta digest (DisplayListDelta): suggestion is hashed only when the capability was negotiated, per render-pipeline: opt-in display-list-v2-diagnostics capability carries suggestion on the v2 wire #358's Appendix A, with Swift golden tests pinned to the same digests as Rust and Python (c4e7c712… on, e554935e… off).

Review round

An independent review found two issues, fixed test first at ef4574d15fae83c0:

  • v2 diagnostics never reached the Problems panel; asRuntimeV1 was test-only. Before the fix, problemsList returned 0 where 1 was expected.
  • The Swift digest hashed suggestions regardless of the capability, with no pinned vectors. Before the fix, capability off gave c4e7c712… where e554935e… was expected.

It also added tests for empty-suggestion handling in both readers and for decline logging.

Tests

  • cargo test --manifest-path crates/document-runtime/Cargo.toml: every result ok (lib 18, chunks 6, display_sibling 10, session 19).
  • swift test --filter 'RenderingV2|PreviewV2|EditorDiagnostics|ProblemsPanel|DisplayListDelta|CommandTable|HostedWindowSupport': Executed 109 tests, with 11 tests skipped and 0 failures.
  • Full swift test --skip NearbyInterfaceTests --skip NearbyLANInterfaceTests: Executed 1224 tests, with 135 tests skipped and 2 failures. Both failures are RustPDFExportPipeTests 30 s load timeouts; --filter RustPDFExportPipeTests alone gives Executed 4 tests, with 0 failures.

Overlap check

Not done

🤖 Generated with Claude Code

https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

d-q222 and others added 9 commits September 14, 2026 00:18
Decode optional code/suggestion/labels/notes/help on RuntimeV1.Diagnostic
(FastJSON + Codable), group/categorise by code with the message-phrase
fallback kept, show notes/help on the row, copy rustc-like = note/= help
lines, and apply help.replacement through the existing QuickFix path when
the range is in bounds and the compile revision still matches.

Filtered swift test from apps/mac (StructuredDiagnosticTests,
EditorDiagnostics*, DiagnosticsPanelTests, FastJSONTests, ProtocolTests):
Executed 76 tests, with 7 tests skipped and 0 failures.

Next: full `swift test` from apps/mac, then push this lane branch.

Open questions: none in-scope; help.replacement with an explicit other-file
path hides Fix… (QuickFix.otherDocument), matching today's explanation edits.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
Add display::Diagnostic.suggestion (None at every constructor) and tests
that expect unknown_command/\alpha and unsupported_feature. from_compiler
still hardcodes code "compiler" and does not copy suggestion.

Ran: CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib from_compiler_forwards
Result: test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 89 filtered out
  display::tests::from_compiler_forwards_code_and_suggestion: left "compiler" right "unknown_command"

Next: forward compiler code (explicit, else default_code, else "compiler")
and suggestion; emit suggestion only in runtime-v1 JSON; extend corpus
owner_for; keep v2 diagnostics without a suggestion key.

Open questions: none in-scope. vendor/, apps, protocol schema untouched.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
from_compiler copies the compiler's code (explicit, else default_code(message),
else "compiler") and suggestion. runtime-v1 JSON emits suggestion only when
set; display-list-v2 diagnostics are unchanged except the code value.
owner_for accepts the compiler code set; tikz_pipeline rejects those codes
too so a forwarded unknown_command cannot slip through as "not compiler".

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- --lib from_compiler_forwards -- writer_matches_value_tree: test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 88 filtered out
- --lib write_json_matches_the_value_tree: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 89 filtered out
- --test compiler_diagnostic_forward: test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- --test tikz_pipeline: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- python3 -m unittest tools.real-world-corpus.test_run: Ran 6 tests in 0.008s OK

Next: full cargo test --manifest-path crates/render-pipeline/Cargo.toml control-vs-after.

Open questions: no in-scope golden embeds "code":"compiler" for a compiler
diagnostic (vendor/pdf fixture and apps/mac display-list fixture are out of
lane). docs/user/compiler.md still shows the old example.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
Add protocol/proposals/display-list-v2-diagnostics.md: capability accepted
only with display-list-v2; suggestion/labels/notes/help use the runtime-v1
shapes with v2 #/$defs/source; omit-when-empty; deltas carry the same keys;
without the cap bytes stay identical. Schema delta is text in the proposal
(frozen rendering-v2.schema.json untouched). labels/notes/help wait for a
vendor re-pin past #346; this lane serialises suggestion only.

Ran: none (docs-only).

Next: failing tests for negotiated vs not, compact vs JSON-tree, and a
suggestion-only delta.

Open questions: none in-scope. No vendor/, schema, apps, or contracts edits.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
CAP_DIAGNOSTICS and Wire.diagnostics / Capabilities.diagnostics, negotiated
only with display-list-v2. Writers still emit the frozen four-key diagnostic,
so capability-on output is byte-identical to capability-off and a suggestion
change does not change the delta header digest.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib
  diagnostics_capability_gates_suggestion -- suggestion_change_is_hashed --
  negotiation_accepts_only_known
Result: test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 89 filtered out
- negotiation_accepts_only_known_requested_capabilities: ok
- diagnostics_capability_gates_suggestion_on_the_wire: left == right (no suggestion key)
- suggestion_change_is_hashed_and_deltaed_only_when_serialised: header_digest equal with cap on

Next: emit suggestion from write_diagnostics / diagnostic_json when the cap
is on and the value is Some; hash it only then.

Open questions: none. labels/notes/help still wait for #346 re-pin.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
write_diagnostics and diagnostic_json_wire add suggestion after sources
when Wire.diagnostics is set and the value is Some (BTreeMap order; never
null). Deltas use the same writer. header_digest hashes suggestion only
then, so a suggestion-only change produces a delta. Frozen writers stay
four-key. No labels/notes/help slots (vendor compiler has none until #346).

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- --lib diagnostics_capability_gates_suggestion -- suggestion_change_is_hashed
  -- write_json_matches: test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 89 filtered out
- --lib writer_matches_value_tree -- from_compiler_forwards --
  negotiation_accepts: test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 89 filtered out
- --test compiler_diagnostic_forward: test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Next: full cargo test --manifest-path crates/render-pipeline/Cargo.toml.

Open questions: none in-scope.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
…d-diagnostics' into agent/daniel-parent/mac-v2-diagnostics

Stacking base for an overnight daniel-parent lane (#277); announced on #2.

Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
Add display-list-v2-diagnostics.json (frozen four-key diagnostic plus one
with suggestion/labels/notes/help), Codable slots on RenderingV2.Diagnostic,
and tests for fast==slow, setLiveV2 negotiation, document-runtime allowlist,
and Fix… from suggestion. Slow JSONDecoder already fills the slots; the
fast reader still skips them, so decode() (fast-first) drops the fields.

Ran:
- apps/mac swift test --filter RenderingV2Tests.testDisplayListV2DiagnosticsFixtureKeepsStructuredFields|…|V2ImageTests.testPaneRequestsImagesAlongsideV2…|EditorDiagnosticsQuickFixTests.testSuggestionReplacementBoundsAndHelpWins|testHelpReplacementBoundsAndRevisionGate
  Executed 5 tests, with 18 failures (0 unexpected) in 0.252 seconds
  testHelpReplacementBoundsAndRevisionGate passed; the four new assertions fail
- CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-macv2 cargo test --manifest-path crates/document-runtime/Cargo.toml --lib diagnostics_capability_is_accepted
  test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 20 filtered out
  unknown accepted capability display-list-v2-diagnostics

Next: fast diagnostic() reads the new keys; setLiveV2 requests the cap
with images; document-runtime allowlist mirrors images; canApplyHelpReplacement
covers suggestion.

Open questions: Mac compile path is WorkerClient / preview-controller, not
document-runtime directly; the allowlist is still the documented reference.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
Fast and slow readers fill suggestion/labels/notes/help (v2 nested source
on help.replacement). setLiveV2 asks for display-list-v2-diagnostics next
to images. document-runtime accepts that cap only with display-list-v2.
Fix… uses suggestion over sources[0] when help.replacement is absent.
Delta header digest hashes suggestion when present (producer rule).

Ran:
- apps/mac swift test --filter RenderingV2Tests|V2ImageTests.testPaneRequestsImagesAlongsideV2…|EditorDiagnosticsQuickFixTests|LayoutCapabilityTests|DiagnosticsPanelTests
  Executed 41 tests, with 1 test skipped and 0 failures (0 unexpected) in 1.172 seconds
- CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-macv2 cargo test --manifest-path crates/document-runtime/Cargo.toml --lib capability_is_accepted
  test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 19 filtered out

Next: filtered RenderingV2*/V2*/EditorDiagnostics*/DiagnosticsPanel*/LayoutNegotiation* then full swift test and cargo test document-runtime.

Open questions: Mac talks WorkerClient and preview-controller; document-runtime is the helper allowlist, not the direct-worker path.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
…no re-derived fallback

Adopts the semantics of GoKubar's duplicate #370: the compiler's constructors
already set `code` via `default_code`, and a `None` is deliberate (request
validation), so re-deriving `default_code(message)` here could label a
diagnostic the compiler's own runtime-v1 reply leaves uncoded. Now
`d.code.map_or("compiler", ...)`.

--lib from_compiler 1/0 (test updated: uncoded stays "compiler");
compiler_diagnostic_forward 2/0 (tikz still unsupported_feature, set by the
compiler's constructor); tikz_pipeline 3/0; golden_v1 1/0.

Implementation-Agent: Claude Code (daniel-parent, mac-m5pro-dq222)
Commit-Executor: Claude Code (daniel-parent)
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
@d-q222

d-q222 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

daniel-muse-lead: ACK, claiming. coord.py claim GH-363 done. Lane based on agent/daniel-parent/mac-v2-diagnostics (not main, since it needs your suggestion-hashing change already there). Rust half + fixture first; will attempt the Swift half and report cleanly if my sandbox can't run it, per your fallback. Will stop and report rather than pick a side if the digests disagree.

@d-q222

d-q222 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

daniel-muse-lead: slice 1 done — Rust half green (header_digests_match_fixture_for_diagnostics_wire, re-run independently by me), fixture in place. Swift half was blocked in the Muse sandbox as expected (SwiftPM's own internal sandboxing can't nest inside the outer one), but since I run outside that sandbox I tried it myself: it built and ran, and found a real (small) bug in the generated Swift test's fixture path — off by one .deletingLastPathComponent() call, resolving to apps/protocol/fixtures/... instead of protocol/fixtures/.... No digest disagreement — the test just couldn't find the file. Sent it back to the lane as slice 2 for the one-line path fix (kept it Muse's fix rather than patching it myself, so provenance stays truthful). Will report once that lands and I can verify the actual digest comparison runs and passes.

@d-q222

d-q222 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

daniel-muse-lead: done, PR #373 (draft, based on agent/daniel-parent/mac-v2-diagnostics). Both languages agree — no digest disagreement. cargo test -p flashtex-render-pipeline: 1/1. swift test --filter DisplayListDeltaDigestTests: 1/1, re-run by me outside the Muse sandbox after fixing the path bug mentioned above.

@d-q222

d-q222 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

daniel-parent: review note 1 (the cross-language digest) is resolved by #373 (daniel-muse-lead). A shared fixture shows Rust header_digest and Swift headerDigest agree on all four diagnostic/suggestion cases; I re-ran both. Suggest merging #373 right after this PR.

…s and set diagnostics: false in flashtex-render's Wire

Conflict in crates/render-pipeline/src/bin/flashtex-render.rs: main (#344) made
--v2 honour --images; this branch added the Wire.diagnostics field. Resolved
as Wire { images: self.images, device_color, diagnostics: false }.
lib 97/0, compiler_diagnostic_forward 2/0, display_list_delta 3/0, cli_e2e 4/0.

Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
…c-v2-diagnostics (main + inventory sync + flashtex-render Wire resolution)

Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
…c-v2-diagnostics (main + inventory sync + flashtex-render Wire resolution)

Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
d-q222 and others added 7 commits September 14, 2026 12:56
…nt digest

Declining display-list-v2 now drops the whole family (images, device-color,
diagnostics, delta, only) via is_display_list_family. The size estimate
charges suggestion only when Wire.diagnostics would serialise it.
Some("") is omitted from the compact writer, JSON tree, and header_digest
through Diagnostic::wire_suggestion. Digest scheme stays dl2-canon-1:
optional negotiated fields are Wire-gated like image items, not a rename
(Mac still only accepts dl2-canon-1). LM skip in compiler_diagnostic_forward
is now an assert. Stale runtime-v1 "does not forward code/suggestion"
wording updated.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib
  test result: ok. 115 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- cargo test --test compiler_diagnostic_forward --test display_list_delta
  compiler_diagnostic_forward: test result: ok. 4 passed; 0 failed; 0 ignored
  display_list_delta: test result: ok. 3 passed; 0 failed; 0 ignored
- python unittest tests/test_rendering_v2.py: Ran 23 tests in 0.039s OK

Next: git fetch origin && git merge origin/main, re-run, push.

Open questions: none on the five review items.

Implementation-Agent: cursor-agent cursor-grok-4.6-high-fast
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
Merge origin/main (Mac editor folding/indent/print, cleveref/includeonly,
kernel inventory tests). No conflicts with the v2 diagnostics capability
work. Re-ran the lane gates after the merge.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib
  test result: ok. 126 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out
- cargo test --test compiler_diagnostic_forward --test display_list_delta
  compiler_diagnostic_forward: test result: ok. 4 passed; 0 failed; 0 ignored
  display_list_delta: test result: ok. 3 passed; 0 failed; 0 ignored
- python unittest tests/test_rendering_v2.py: Ran 23 tests in 0.037s OK

Next: push the lane branch.

Open questions: none.

Implementation-Agent: cursor-agent cursor-grok-4.6-high-fast
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
Appendix A header_digest now hashes suggestion at the same position and
with the same encoding as delta.rs (only when display-list-v2-diagnostics
is negotiated and the value is non-empty). Pinned
c4e7c7129994d1b73c8dfe3d9b1b9a0cbf0edc49e7f9e6bc848d8c66e0126bb6 from a
hand walk of that algorithm; diagnostics-off in-memory suggestion matches
the no-suggestion digest. Overflow no longer echoes -images or
-device-color.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib --test display_list_delta --test compiler_diagnostic_forward
  lib: test result: ok. 128 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out
  compiler_diagnostic_forward: test result: ok. 5 passed; 0 failed; 0 ignored
  display_list_delta: test result: ok. 3 passed; 0 failed; 0 ignored
- /tmp/flashtex-schema-venv/bin/python -m unittest tests.test_rendering_v2
  Ran 24 tests in 0.039s OK

Next: push this checkpoint for daniel-parent re-review of the last PARTIAL.

Open questions: none.

Implementation-Agent: cursor-agent cursor-grok-4.6-high-fast
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
…d-diagnostics' into agent/daniel-parent/mac-v2-diagnostics
… flag

Write the review-gap tests first. A v2 frame with a suggestion and an empty
compile_result list still yields problemsList.count == 0 (caretFix and
EditorDiagnostics cannot see it). headerDigest now takes
diagnosticsCapability but still hashes a present suggestion, so the #358
capability-off golden e554935e… fails (got c4e7c712…). Empty-suggestion
normalisation and decline logging for display-list-v2-diagnostics (including
whole-family overflow) already pass.

Ran (apps/mac):
- swift test --filter 'ProblemsPanelV2Tests|RenderingV2Tests'
  Executed 23 tests, with 1 test skipped and 5 failures
  (testV2SuggestionWithEmptyV1… and hosted panel: problemsList 0 != 1;
   testHeaderDigestMatchesAppendixA… capability-off digest mismatch)

Next: map v2 diagnostics through asRuntimeV1 into displayedDiagnostics
(prefer v1 when non-empty, never duplicate) and hash suggestion only when
the diagnostics capability is accepted.

Open questions: none.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
@d-q222

d-q222 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Checkpoint ef4574d1 on agent/daniel-parent/mac-v2-diagnostics.

Done: failing tests for the independent review (REQUEST_CHANGES). A v2 frame with a suggestion and an empty v1 list still yields problemsList.count == 0. headerDigest takes diagnosticsCapability but still hashes a present suggestion, so the #358 capability-off golden fails (c4e7c712… vs e554935e…). Empty-suggestion normalisation ("" → nil on both readers) and decline logging for display-list-v2-diagnostics (including whole-family overflow) already pass.

Results: swift test --filter 'ProblemsPanelV2Tests|RenderingV2Tests'Executed 23 tests, with 1 test skipped and 5 failures.

Next: map v2 diagnostics through asRuntimeV1 into displayedDiagnostics (prefer v1 when non-empty, never duplicate) and hash suggestion only when the diagnostics capability is accepted.

Map a live v2 frame through asRuntimeV1 into displayedDiagnostics (and
therefore problemsList, caretFix, and editor marks) when compile_result
lists none; runtime-v1 wins when both sources carry rows so they are
never concatenated. headerDigest hashes suggestion only when
display-list-v2-diagnostics was accepted, matching Appendix A / #358.
The live delta install path passes that flag from the applied result.

Ran:
- CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-macv2 cargo test --manifest-path crates/document-runtime/Cargo.toml
  lib: test result: ok. 18 passed; 0 failed; 3 ignored
  chunks: test result: ok. 6 passed
  display_sibling: test result: ok. 10 passed
  session: test result: ok. 19 passed; 0 failed; 1 ignored
- swift test --filter 'RenderingV2|PreviewV2|EditorDiagnostics|ProblemsPanel|DisplayListDelta|CommandTable|HostedWindowSupport'
  Executed 109 tests, with 11 tests skipped and 0 failures
- swift test --skip NearbyInterfaceTests --skip NearbyLANInterfaceTests
  Executed 1224 tests, with 135 tests skipped and 2 failures (RustPDFExportPipeTests load timeout)
  re-run --filter RustPDFExportPipeTests in isolation: Executed 4 tests, with 0 failures

Next: daniel-parent review of the REQUEST_CHANGES fix-up.

Open questions: none.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>
@d-q222

d-q222 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Checkpoint 5fae83c0 on agent/daniel-parent/mac-v2-diagnostics.

Done: v2 frame diagnostics map through asRuntimeV1 into displayedDiagnostics / problemsList / caretFix / editor marks when the v1 list is empty. Runtime-v1 wins when both sources carry rows (never concatenated). headerDigest hashes suggestion only when display-list-v2-diagnostics was accepted; goldens match #358 (c4e7c712… / e554935e…). Empty "" → nil already held on both readers; decline logging for the diagnostics cap and whole-family overflow already held.

Results:

  • CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-macv2 cargo test --manifest-path crates/document-runtime/Cargo.toml — lib ok. 18 passed; 0 failed; 3 ignored; chunks ok. 6 passed; display_sibling ok. 10 passed; session ok. 19 passed; 0 failed; 1 ignored
  • swift test --filter 'RenderingV2|PreviewV2|EditorDiagnostics|ProblemsPanel|DisplayListDelta|CommandTable|HostedWindowSupport'Executed 109 tests, with 11 tests skipped and 0 failures
  • swift test --skip NearbyInterfaceTests --skip NearbyLANInterfaceTestsExecuted 1224 tests, with 135 tests skipped and 2 failures (RustPDFExportPipeTests load timeout under the full suite). Isolation re-run --filter RustPDFExportPipeTests: Executed 4 tests, with 0 failures.

Next: daniel-parent review. Did not change #358 producer files.

@d-q222 d-q222 changed the title mac: request display-list-v2-diagnostics and show v2 suggestions in the Problems panel, with Fix… (#277) mac: consume display-list-v2-diagnostics — v2 suggestions reach Problems and Tab-to-fix (after #358) Sep 14, 2026
@d-q222
d-q222 marked this pull request as ready for review September 14, 2026 20:52
GoKubar pushed a commit that referenced this pull request Sep 15, 2026
… gaps (ordering only, on #476) (#411)

* wip(#76): failing tests for Problems list bucket order

Add DiagnosticsPanelTests that pin errors-then-warnings-then-gaps
across two documents, Copy Diagnostics walking that order, and
⌘⌥] starting on the first author error rather than an earlier gap.

What was run: `swift test --filter` of the three new methods in
apps/mac. Result: Executed 3 tests, with 7 failures (0 unexpected)
in 0.238 seconds. Current groups(of:) still sorts by first
occurrence in source, so the tikz gap leads the list.

Next: sort groups(of:) by bucket while keeping document order
inside each bucket and inside each group's occurrences; add
summary() for the header counts; wire ProblemsPanel VoiceOver.

Open questions: whether ProblemsPanel.idealHeight 260 (~1/3 of a
typical window) still needs a smaller default; overlap with #363/#367/#403.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* wip(#76): sort Problems groups by bucket and show header counts

groups(of:) lists author errors, then warnings, then FlashTeX gaps,
keeping document order inside each bucket and inside each group's
occurrences. result.diagnostics is not reordered. summary() reuses
counts() for "2 errors · 5 warnings · 46 FlashTeX gaps"; the Problems
header and VoiceOver label read that string. Opening height is 180 pt
(was 260, ~1/3 of a typical window); the 40% cap is unchanged.

What was run: swift test --filter DiagnosticsPanelTests|
EditorDiagnostics*|WorkspaceShellTests in apps/mac.
Result: Executed 54 tests, with 7 tests skipped and 0 failures
(0 unexpected) in 2.107 seconds.

Next: full `swift test` from apps/mac; overlap check; push.

Open questions: none on ordering/header. Existing AppStorage values
of 260 remain until the user resizes.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* wip(#76): restore coloured Problems header count chips

Restore the three SF Symbol count labels (errors red, warnings orange,
gaps puzzle-piece secondary) including zeros so an all-gap document still
shows the error column. VoiceOver uses summary() once via .ignore on the
chips; the panel stays .contain so the list remains accessible.
DiagnosticsListView caption matches. idealHeight stays 180; a stored
AppStorage 260 cannot be told from a user resize, so it is left as-is.

What was run: swift test --filter 'DiagnosticsPanelTests|EditorDiagnostics|WorkspaceShellTests'
in apps/mac.
Result: Executed 59 tests, with 7 tests skipped and 0 failures (0 unexpected) in 2.112 seconds.

Next: daniel-parent review / PR.

Open questions: none on the header. AppStorage 260 left as-is.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
@GoKubar

GoKubar commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Refreshed onto current main (2d1b517). One conflict, crates/render-pipeline/src/protocol.rs: main's #651 rewrote the over-limit refusal to avoid serialising the 16 MB envelope, while this stack (#358) widens the capability filter to the display-list family. Resolved as the union: #651's non-serialising envelope_len pre-check structure with #358's !is_display_list_family(c) predicate replacing c != CAP_DISPLAY_LIST. Rust side verified: cargo test --release --no-run on crates/render-pipeline builds every test target with 0 errors (the identical resolution passes the full render-pipeline suite, 457/457, on the integration branch for #649's sibling queue). Swift side needs the mac CI job. Note: this branch contains #358 — merging #358 first (it is in integration queue 3) will shrink this diff to the mac-side delta.

GoKubar and others added 2 commits September 15, 2026 18:35
crates/perf-bench (#206) constructs v1::Capabilities literally; the
#358 half of this stack adds the diagnostics field, so the bench failed
to compile (E0063) and the engine performance job failed at Build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5Hmwh5GHNTiAmHUJ1MZu
@GoKubar

GoKubar commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Re-refreshed onto main after #654/#294/#295/#296 landed (main is now 2890090). Three new conflicts, all from #294's windowing capability meeting this stack's diagnostics capability, resolved as unions:

  • crates/perf-bench/src/measure.rs — both sides add a field to the Capabilities literal; kept both (diagnostics: false, window: false).
  • crates/render-pipeline/src/v1.rs — main rewrote the CAP_DELTA guard as with_display_list && !windowing; this stack adds a CAP_DIAGNOSTICS arm. Kept main's new delta guard and rewrote the diagnostics arm to use the same with_display_list binding rather than re-scanning requested.
  • crates/render-pipeline/src/protocol.rs — main open-codes the refusal filter as c != CAP_DISPLAY_LIST && c != CAP_V2_ONLY && c != CAP_WINDOW; render-pipeline: opt-in display-list-v2-diagnostics capability carries suggestion on the v2 wire #358 introduced is_display_list_family for exactly this. Kept the predicate and added CAP_WINDOW to it, so the behaviour matches main's filter and there is one list instead of two.

GoKubar added a commit that referenced this pull request Sep 15, 2026
…#658)

* wip(#277): reproduce from_compiler dropping code and suggestion

Add display::Diagnostic.suggestion (None at every constructor) and tests
that expect unknown_command/\alpha and unsupported_feature. from_compiler
still hardcodes code "compiler" and does not copy suggestion.

Ran: CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib from_compiler_forwards
Result: test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 89 filtered out
  display::tests::from_compiler_forwards_code_and_suggestion: left "compiler" right "unknown_command"

Next: forward compiler code (explicit, else default_code, else "compiler")
and suggestion; emit suggestion only in runtime-v1 JSON; extend corpus
owner_for; keep v2 diagnostics without a suggestion key.

Open questions: none in-scope. vendor/, apps, protocol schema untouched.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* wip(#277): forward compiler diagnostic code and suggestion

from_compiler copies the compiler's code (explicit, else default_code(message),
else "compiler") and suggestion. runtime-v1 JSON emits suggestion only when
set; display-list-v2 diagnostics are unchanged except the code value.
owner_for accepts the compiler code set; tikz_pipeline rejects those codes
too so a forwarded unknown_command cannot slip through as "not compiler".

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- --lib from_compiler_forwards -- writer_matches_value_tree: test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 88 filtered out
- --lib write_json_matches_the_value_tree: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 89 filtered out
- --test compiler_diagnostic_forward: test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- --test tikz_pipeline: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- python3 -m unittest tools.real-world-corpus.test_run: Ran 6 tests in 0.008s OK

Next: full cargo test --manifest-path crates/render-pipeline/Cargo.toml control-vs-after.

Open questions: no in-scope golden embeds "code":"compiler" for a compiler
diagnostic (vendor/pdf fixture and apps/mac display-list fixture are out of
lane). docs/user/compiler.md still shows the old example.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* wip(#277): propose display-list-v2-diagnostics capability

Add protocol/proposals/display-list-v2-diagnostics.md: capability accepted
only with display-list-v2; suggestion/labels/notes/help use the runtime-v1
shapes with v2 #/$defs/source; omit-when-empty; deltas carry the same keys;
without the cap bytes stay identical. Schema delta is text in the proposal
(frozen rendering-v2.schema.json untouched). labels/notes/help wait for a
vendor re-pin past #346; this lane serialises suggestion only.

Ran: none (docs-only).

Next: failing tests for negotiated vs not, compact vs JSON-tree, and a
suggestion-only delta.

Open questions: none in-scope. No vendor/, schema, apps, or contracts edits.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* wip(#277): reproduce v2 suggestion omitted without diagnostics cap

CAP_DIAGNOSTICS and Wire.diagnostics / Capabilities.diagnostics, negotiated
only with display-list-v2. Writers still emit the frozen four-key diagnostic,
so capability-on output is byte-identical to capability-off and a suggestion
change does not change the delta header digest.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib
  diagnostics_capability_gates_suggestion -- suggestion_change_is_hashed --
  negotiation_accepts_only_known
Result: test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 89 filtered out
- negotiation_accepts_only_known_requested_capabilities: ok
- diagnostics_capability_gates_suggestion_on_the_wire: left == right (no suggestion key)
- suggestion_change_is_hashed_and_deltaed_only_when_serialised: header_digest equal with cap on

Next: emit suggestion from write_diagnostics / diagnostic_json when the cap
is on and the value is Some; hash it only then.

Open questions: none. labels/notes/help still wait for #346 re-pin.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* wip(#277): emit v2 suggestion when display-list-v2-diagnostics is on

write_diagnostics and diagnostic_json_wire add suggestion after sources
when Wire.diagnostics is set and the value is Some (BTreeMap order; never
null). Deltas use the same writer. header_digest hashes suggestion only
then, so a suggestion-only change produces a delta. Frozen writers stay
four-key. No labels/notes/help slots (vendor compiler has none until #346).

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- --lib diagnostics_capability_gates_suggestion -- suggestion_change_is_hashed
  -- write_json_matches: test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 89 filtered out
- --lib writer_matches_value_tree -- from_compiler_forwards --
  negotiation_accepts: test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 89 filtered out
- --test compiler_diagnostic_forward: test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Next: full cargo test --manifest-path crates/render-pipeline/Cargo.toml.

Open questions: none in-scope.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* render-pipeline: from_compiler forwards exactly the compiler's code, no re-derived fallback

Adopts the semantics of GoKubar's duplicate #370: the compiler's constructors
already set `code` via `default_code`, and a `None` is deliberate (request
validation), so re-deriving `default_code(message)` here could label a
diagnostic the compiler's own runtime-v1 reply leaves uncoded. Now
`d.code.map_or("compiler", ...)`.

--lib from_compiler 1/0 (test updated: uncoded stays "compiler");
compiler_diagnostic_forward 2/0 (tikz still unsupported_feature, set by the
compiler's constructor); tikz_pipeline 3/0; golden_v1 1/0.

Implementation-Agent: Claude Code (daniel-parent, mac-m5pro-dq222)
Commit-Executor: Claude Code (daniel-parent)
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

* compiler: accept kernel preamble declarations as no-ops, warn on DocumentMetadata keys

\NeedsTeXFormat, \ProvidesClass, \ProvidesPackage and \ProvidesFile are
.cls/.sty declarations with no visible output in a document compiler,
so they're accepted silently (required group, then the optional
[info]/[date] bracket, both discarded).

\DocumentMetadata (LaTeX2e 2022+) is different: its keys (pdfstandard,
pdfversion, lang, testphase, ...) really do change real LaTeX's PDF
output, which this compiler doesn't generate that way. Before
\documentclass it's accepted with a warning naming the ignored keys;
after \documentclass it's a real error, matching real LaTeX. Reuses
the existing document_class: Option<String> field (set when
\documentclass itself is processed) for the position check -- no new
preamble-tracking mechanism needed.

Regenerates the inventory artifacts and the Mac bundled copy.

Implementation-Agent: muse-spark-1.3-contributor (Muse Code, lane kernel-text-symbols, slice 1)
Commit-Executor: daniel-muse-lead (Claude Sonnet)
Reviewed-by: daniel-muse-lead (Claude Sonnet)
Muse-Lane-Head: 3dbf61de
Co-authored-by: muse-spark-1.3-contributor <muse-contributor@flashtex.invalid>

* wip(#358): negotiated-case protocol test next to #354's omit-suggestion

compiler_diagnostic_forward.rs: keep typo_alpah_is_unknown_command_with_alpha_suggestion_in_v1_only
(v2 omits suggestion when the cap is off). Add negotiated_v2_diagnostics_emits_suggestion
through handle_line: capability off omits suggestion; with display-list-v2 +
display-list-v2-diagnostics the sibling carries suggestion and echoes the cap;
the cap alone is rejected. Proposal status no longer says stacked on #354.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib
  test result: ok. 112 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- cargo test --manifest-path crates/render-pipeline/Cargo.toml --test compiler_diagnostic_forward --test display_list_delta
  compiler_diagnostic_forward: test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
  display_list_delta: test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- python unittest tests/test_rendering_v2.py: Ran 23 tests in 0.037s OK

Next: push this lane branch.

Open questions: none. document-runtime allowlist remains #363.

Implementation-Agent: cursor-agent cursor-grok-4.6
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* wip(#358): failing tests for overflow dependents, estimate, empty suggestion

Reproduction tests for the independent review: declining display-list-v2
must also drop display-list-v2-diagnostics (FLASHTEX_MAX_REPLY_BYTES=6000);
the size estimate with diagnostics off must match a suggestion-stripped
list; Some("") must be omitted from the writer and the digest.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- cargo test --lib estimate_omits_suggestion
  test result: FAILED. 0 passed; 1 failed (784 vs 758)
- cargo test --lib empty_suggestion
  test result: FAILED. 0 passed; 2 failed
- cargo test --lib empty_suggestion_is_not_hashed
  test result: FAILED. 0 passed; 1 failed
- cargo test --test compiler_diagnostic_forward declining_display_list
  test result: FAILED. 0 passed; 1 failed
  (echoed ["display-list-v2-diagnostics"] with no sibling)

Next: drop dependents with one helper, gate the estimate on Wire,
omit empty suggestions in writer and digest, document dl2-canon-1
conditional suggestion, make the LM skip visible.

Open questions: digest scheme — document conditional suggestion under
dl2-canon-1 (images already Wire-gate the same scheme) rather than
rename to dl2-canon-1+diagnostics (Mac still only knows dl2-canon-1).

Implementation-Agent: cursor-agent cursor-grok-4.6-high-fast
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* compiler: brace-aware DocumentMetadata keys, seen-documentclass flag

Fixes all three of daniel-parent's re-review points on #440:

1. \DocumentMetadata{testphase={phase-III,math,table}} now warns
   naming only "testphase" (a top-level key with a braced value), not
   three separate "keys" -- keys are now split on top-level commas,
   tracking brace depth character by character, instead of a plain
   comma split over the raw (brace-stripped) argument text.
2. A new seen_documentclass flag (set on any \documentclass
   invocation, even an empty one) replaces document_class.is_some()
   for the \DocumentMetadata position check, so
   \documentclass{}\DocumentMetadata{lang=en-US} is now the real
   "must come before \documentclass" error instead of a warning.
3. missing_required_argument_is_a_parse_error now asserts the exact
   message this PR's own dispatch arm produces, instead of a generic
   "command not supported" message the unmodified parser already
   emits for any unknown preamble command.

Mutation-tested for all three (revert -> new/strengthened test fails
-> restore -> passes), independently spot-checked by the reviewer for
fix 1 (disabling brace-depth tracking fails
document_metadata_nested_braces_are_a_single_key as predicted).

cargo test --test kernel_preamble_declarations: 8 passed, 0 failed.
Full cargo test in crates/compiler: 0 FAILED across all suites.

Implementation-Agent: muse-spark-1.3-contributor (Muse Code, lane fix440, slice 1)
Commit-Executor: daniel-muse-lead (Claude Sonnet)
Reviewed-by: daniel-muse-lead (Claude Sonnet)
Co-authored-by: muse-spark-1.3-contributor <muse-contributor@flashtex.invalid>

* wip(#358): drop dependents on overflow; omit empty suggestion; document digest

Declining display-list-v2 now drops the whole family (images, device-color,
diagnostics, delta, only) via is_display_list_family. The size estimate
charges suggestion only when Wire.diagnostics would serialise it.
Some("") is omitted from the compact writer, JSON tree, and header_digest
through Diagnostic::wire_suggestion. Digest scheme stays dl2-canon-1:
optional negotiated fields are Wire-gated like image items, not a rename
(Mac still only accepts dl2-canon-1). LM skip in compiler_diagnostic_forward
is now an assert. Stale runtime-v1 "does not forward code/suggestion"
wording updated.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib
  test result: ok. 115 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- cargo test --test compiler_diagnostic_forward --test display_list_delta
  compiler_diagnostic_forward: test result: ok. 4 passed; 0 failed; 0 ignored
  display_list_delta: test result: ok. 3 passed; 0 failed; 0 ignored
- python unittest tests/test_rendering_v2.py: Ran 23 tests in 0.039s OK

Next: git fetch origin && git merge origin/main, re-run, push.

Open questions: none on the five review items.

Implementation-Agent: cursor-agent cursor-grok-4.6-high-fast
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* wip(#358): pin Appendix A suggestion digest; drop overflow dependents

Appendix A header_digest now hashes suggestion at the same position and
with the same encoding as delta.rs (only when display-list-v2-diagnostics
is negotiated and the value is non-empty). Pinned
c4e7c7129994d1b73c8dfe3d9b1b9a0cbf0edc49e7f9e6bc848d8c66e0126bb6 from a
hand walk of that algorithm; diagnostics-off in-memory suggestion matches
the no-suggestion digest. Overflow no longer echoes -images or
-device-color.

Ran (CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-diagfwd):
- cargo test --manifest-path crates/render-pipeline/Cargo.toml --lib --test display_list_delta --test compiler_diagnostic_forward
  lib: test result: ok. 128 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out
  compiler_diagnostic_forward: test result: ok. 5 passed; 0 failed; 0 ignored
  display_list_delta: test result: ok. 3 passed; 0 failed; 0 ignored
- /tmp/flashtex-schema-venv/bin/python -m unittest tests.test_rendering_v2
  Ran 24 tests in 0.039s OK

Next: push this checkpoint for daniel-parent re-review of the last PARTIAL.

Open questions: none.

Implementation-Agent: cursor-agent cursor-grok-4.6-high-fast
Commit-Executor: cursor-agent
Lane-Owner: daniel-parent (mac-m5pro-dq222)
Co-authored-by: Cursor <cursoragent@cursor.com>

* fix440: keep text glued after ']', honour escaped commas in DocumentMetadata

- optional_bracket_argument consumes exactly up to the matching ']' at
  brace depth 0 and rewrites any trailing tail back into the token stream
  (fixes ProvidesFile{foo.cfg}[2024]VISIBLE losing VISIBLE). The helper
  predates this PR (on main since 01fb13c); all ~25 call sites benefit.
- document_metadata treats a standalone one-character Word ',', '{', '}'
  (i.e. control symbols) as escaped literals via placeholders while
  splitting top-level commas (fixes foo=hello\,world reporting 'world').
- document_metadata_after_documentclass_is_an_error now asserts this PR's
  exact ordering message, not just any error mentioning DocumentMetadata.

Implementation-Agent: muse-spark-1.3-contributor
Commit-Executor: muse-spark-1.3-contributor

* compiler: fix two real DocumentMetadata/bracket-argument bugs, merge main

Fixes daniel-parent's third re-review of #440:

1. `optional_bracket_argument` (parser.rs): [ and ] are not lexer-special
   characters, so `[2024]VISIBLE` lexes as one Word token, and the old
   code consumed the whole token, dropping `VISIBLE`. Now scans
   token-by-token for the closing `]` at brace depth 0, and rewrites any
   unconsumed tail back into the token stream in place. Confirmed
   pre-existing beyond DocumentMetadata: ~25 call sites share this
   helper (cite, bibitem, linebreak, footnote, newtheorem, lstlisting,
   ...), all fixed at the helper. Also newly correct: a `]` inside a
   nested `{...}` group no longer closes the bracket argument early.
2. `document_metadata` (parser.rs): a control symbol like `\,`/`\{`/`\}`
   lexes as its own standalone one-character Word token, while a real
   separator character is always embedded inside a longer Word token --
   a reliable signal this fix uses to hide escaped characters behind
   private-use-area placeholders during the top-level comma/brace scan,
   restoring them afterward, so \DocumentMetadata{foo=hello\,world,
   lang=en} no longer reports "world" as a spurious key.
3. document_metadata_after_documentclass_is_an_error now asserts the
   exact ordering-error message, not any error mentioning
   DocumentMetadata.

Merged current main and regenerated all inventory artifacts via the
project scripts (not hand-merged).

Verified independently, including a mutation check on fix 2 (disabling
the escaped-comma placeholder fails document_metadata_escaped_comma_is_
not_a_separator as predicted; restoring passes):
- cargo test --test kernel_preamble_declarations: 10 passed, 0 failed.
- cargo test --test supported_latex: 8 passed, 0 failed.
- Full cargo test in crates/compiler: 0 FAILED across all suites.

Implementation-Agent: muse-spark-1.3-contributor (Muse Code, lane fix440v2, slice 1)
Commit-Executor: daniel-muse-lead (Claude Sonnet)
Reviewed-by: daniel-muse-lead (Claude Sonnet)
Co-authored-by: muse-spark-1.3-contributor <muse-contributor@flashtex.invalid>

* compiler: split DocumentMetadata's plain top-level commas regardless of whitespace

A standalone Word(",") from the lexer was always treated as an escaped `\,`,
so `\DocumentMetadata{foo=bar , lang=en}` lost `lang`: a real separator comma
surrounded by spaces lexes identically to an escaped one. The lexer gives
both the same TokenKind::Word variant, so distinguish them the way
optional_bracket_argument already does one function away: an escaped
control symbol's source span is longer than its one-character text (it
includes the backslash), while a plain word's span length always equals its
text length. Applied the same check to `,`, `{`, `}`.

Added regression tests for the shared optional_bracket_argument bracket
reader across citation, footnote, \\[<length>], theorem-head and listing
option callers, proving trailing text still survives after the bracket
closes.

Implementation-Agent: muse-spark-1.3-contributor
Commit-Executor: daniel-muse-lead (Claude Sonnet)
Reviewed-by: daniel-muse-lead (Claude Sonnet)
Co-authored-by: muse-spark-1.3-contributor <muse-contributor@flashtex.invalid>

* compiler: document macro-comma limit, keep only real ]-glued regression tests

Documented the accepted macro-produced-comma limitation (a comma from
expanding a user macro carries the macro invocation's span, so the
span-length check can't tell it apart from a real separator) at the
span-length check site.

Replaced the caller-regression tests added for optional_bracket_argument
with ones that actually exercise the bug this PR fixes: text glued
directly after the closing ']', not after a following required argument.
The previous \cite/\footnote tests put TEXT after a required {...}, so
they passed even on the pre-fix helper and proved nothing; verified against
the pre-fix helper directly and kept only the ]-glued cases that fail on it
(\ProvidesFile{f}[i]TEXT), since \\[<length>] and \item[<label>] already
had their own tail-rewrite predating this PR.

Implementation-Agent: muse-spark-1.3-contributor
Commit-Executor: daniel-muse-lead (Claude Sonnet)
Reviewed-by: daniel-muse-lead (Claude Sonnet)
Co-authored-by: muse-spark-1.3-contributor <muse-contributor@flashtex.invalid>

* compiler tests: pin text glued after \\[2pt] line-break length

skip_line_break_length has its own tail-rewrite for [2pt]TEXT-style
glued text, separate from optional_bracket_argument; this pins that
behavior next to the sibling ]-glued coverage.

Implementation-Agent: muse-spark-1.3-contributor

* compiler tests: pin text glued after explicit item label

item_label_argument has its own tail-rewrite for [x]TEXT-style glued
text, separate from optional_bracket_argument; this pins that behavior
next to the other explicit-label coverage in list_structure.rs.

Implementation-Agent: muse-spark-1.3-contributor

* compiler: large-document bench and fixture output-identity digests (#65)

What changed:
- src/bin/large_doc_bench.rs: deterministic ~300-page article (sections,
  labels/refs, cites + thebibliography, equation/align*, itemize/enumerate,
  tabular); median-of-N cold and one-character-edit timings through
  Session and protocol::handle_line, plus a reply digest.
- src/bin/fixture_digest.rs: per-fixture SHA-256 of the clean compile dump
  (blocks, diagnostics, pages), the protocol reply, and a warm edited
  Session output, for byte-identity checks across perf changes.

What was run:
large document: 637761 bytes, 450 sections, 5912 blocks, 302 pages, 0 diagnostics, profile=release
fixture_digest over fixtures/ and crates/compiler/tests: 273 files, two runs identical

Next step: profile and fix the top hotspots.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

* compiler: memoise Core 14 shaping, index glyph tables, lend cleveref config (#65)

What changed:
- layout: shaping a Core 14 face is a pure function of (face, text); the
  width/source-bounds/missing-glyph summary layout reads is memoised per
  thread (reset past 131072 entries). Every cross-reference pass used to
  reshape every word with a fully allocated `Shaped`.
- char_table: sorted binary-search index equal to `iter().find` (first
  occurrence) for lm_math/newcm_math/amssymb advances and the export
  Symbol/WinAnsi tables; math_font walked ~440 entries per ASCII character.
- layout: inline_box (every tabular entry) lends the cleveref config to the
  detached cursor instead of building the default name table and cloning.

What was run:
fixture_digest over fixtures/ + crates/compiler/tests (273 files) and the 302-page doc: identical to origin/main 36fe7ec
cargo test (debug): 59 suites, passed=671 failed=0 ignored=5
cargo fmt --check: ok; clippy -D warnings: 20 pre-existing errors, none in touched code

Next step: interleaved A/B timings, PR.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

* compiler: undo whole-crate reformatting swept into the previous commit (#65)

What changed:
- The previous commit accidentally included a local `cargo fmt` of the whole
  crate (43 unrelated files). Every file outside the perf change is restored
  to origin/main 36fe7ec; the perf edits are re-applied without reformatting.
  No behaviour change relative to the previous commit.

What was run:
git diff 36fe7ec --stat: 10 files changed, 607 insertions(+), 42 deletions(-)
fixture_digest (273 files + 302-page doc): identical to origin/main

Next step: interleaved A/B timings, PR.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

* pdf: exact route rounds colours to xcolor's 5 decimals and paints TikZ path items

What changed:
- v2: `pdf_number` formats an f64 as a bounded PDF number (shortest decimal
  rounded half away from zero, no exponent, -0 -> 0); sRGB colour components
  use 5 digits (pdflatex measured: RGB{20,80,170} -> 0.07843 0.31374 0.66667
  rg). Replaces the exact-binary requirement that refused 0.8 and 0.07843.
- v2: `path_fill` / `path_stroke` items (with clips) become
  q [rg RG] [clip W n] [w M d J j] path f|f*|S Q, in pgf's pdfTeX order.
- exact: `M` (miter limit) joins the bounded operator set.

What was run:
- cargo test (crates/pdf): lib 53 passed; tests/v2 7 passed; all targets 0 failed
- cargo check --tests (crates/rendering-core): ok

Next step: re-pin crates/render-pipeline/vendor/pdf so `flashtex build` uses it.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

* pdf: exact route paints alpha through pgf's /pgf@CA and /pgf@ca ExtGStates

What changed:
- exact: Op::StrokeAlpha / Op::FillAlpha, written `/pgf@CA<a> gs` and
  `/pgf@ca<a> gs`, the names and one-key dictionaries pgf's pdfTeX driver
  writes (measured with pdflatex 1.40). `gs` joins the bounded operator set
  for those names only; any other ExtGState is still refused. Validation
  refuses an alpha outside [0, 1] and `gs` during path construction. Each
  page declares the states it selects once, sorted by name, inline:
  `/ExtGState << /pgf@ca0.4 << /ca 0.4 >> >>`.
- v2: a paint alpha below 1 is no longer an error. It is rounded like a
  colour component and selected right after the colour inside the item's
  `q … Q`: path_stroke sets CA, path_fill, rule and glyph_run set ca.
- Tests: exact operator stream and resources for stroke and fill alpha,
  per-page dedup, refusal of other gs names, text alpha on a glyph run.

What was run:
- cargo test (crates/pdf): test result: ok. 53 passed; 0 failed (lib),
  12 passed (tests/exact), 5 (images), 11 (navigation), 28 (render),
  3 (type1), 10 passed; 0 failed (tests/v2).
- cargo check --tests (crates/rendering-core): Finished.

Next step: build extended/tikz-clipping-patterns with a scratch copy whose
render-pipeline/vendor/pdf is this crate.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

* compiler: ignored large-document timing test; bench reports first compile (#65)

What changed:
- tests/large_document_timing.rs: #[ignore] timing test sharing the bench's
  302-page generator; asserts >= 300 pages, no diagnostics, and warm edit ==
  clean compile.
- large_doc_bench prints the first compile in the process (empty memo).

What was run:
cargo test --release --test large_document_timing -- --ignored: 1 passed (302 pages)
cargo test (debug): 60 suites, passed=671 failed=0 ignored=6

Next step: A/B timings, PR.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

* compiler: stop copying placed text, lend the session output, serialise pages once (#65)

What changed:
- layout: clean layout and every cross-reference pass call a new
  non-collecting render_block; only incremental reuse collects placed items.
- layout: the shaping memo hashes a word once per hit (get, not
  contains_key plus index).
- incremental: Session::compile_project_borrowed lends the retained output;
  compile_project_with clones from it as before. protocol uses the borrow.
- protocol: bound_pages serialises each page once and joins the result
  instead of serialising, cloning and serialising again.
- parser: parse_stream handles space, comment and word tokens borrowed
  before cloning the token.

What was run:
fixture_digest (273 fixtures + 302-page doc) vs #535 head 3c2b0e1: IDENTICAL
large_doc_bench reply sha256 51e4d9a9dc3f0ac0aa8cc90267fb8a73664650d2ebf246bcbb2958ad1cba9747 (same as base)
large_doc_bench 5 (load ~44): cold session 50.9 -> 40.6 ms, cold protocol 68.7 -> 46.7, edit protocol 69.9 -> 50.0

Next step: full test run, interleaved median-of-5 timings, PR stacked on #535.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

* pdf: support TikZ tiling patterns

What changed:
- Add deterministic PatternType 1 tiling resources and typed /Pattern cs, scn operators to the exact PDF route.
- Generate the eight requested PGF cells with pattern color, accept the v2 path_fill pattern extension, and cover PaintType 2 RGB selection.
- Add operator, resource, parser, deterministic-output, structure-check, and Ghostscript-backed sample verification coverage.

What was run:
- CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-pattern CARGO_BUILD_JOBS=4 cargo check --manifest-path crates/pdf/Cargo.toml
- CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-pattern CARGO_BUILD_JOBS=4 cargo test --manifest-path crates/pdf/Cargo.toml --lib --tests
- CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-pattern CARGO_BUILD_JOBS=4 cargo test --manifest-path crates/vector-graphics/Cargo.toml --lib tikz::tests::unsupported_input_is_reported_not_dropped
- /opt/homebrew/bin/gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -r144 on /private/tmp/flashtex-pattern-sample.pdf

Next step:
- Carry pattern name/color state through vector-graphics, render-pipeline display items, and display-list JSON so the live TikZ corpus can populate the new PDF contract.

Implementation-Agent: codex gpt-5.6-luna (max)
Commit-Executor: codex (this session)
Lane-Owner: daniel-parent (mac-m5pro-dq222)

* compiler: stop dropping six amsfonts symbols on a false premise

gen_amssymb.py carried SKIP = {angle, hbar, mho, sqsubset, sqsupset,
rightleftharpoons}, justified as "kernel commands amsfonts only
redefines keep their kernel glyphs". That was wrong twice over (checked
against pdfTeX 3.141592653-2.6-1.40.27, TeX Live 2025):

- \mho, \sqsubset, \sqsupset are not kernel commands at all: latex.ltx
  makes them \not@base stubs and amsfonts.sty 99-101 is what provides
  them. FlashTeX diagnosed them "not supported in math mode" even with
  amssymb loaded.
- \angle, \hbar, \rightleftharpoons are kernel composites that amsfonts
  replaces with one msam/msbm glyph of different metrics (\angle
  6.37344pt -> 7.22223pt, \hbar 5.76172pt -> 5.40280pt at 10pt). With
  the package loaded, \angle/\hbar fell through to bare COMMAND_GLYPHS
  rows at Latin Modern advances matching neither pdflatex answer, and
  \rightleftharpoons was "not supported".

Removing SKIP lets the generator emit all six with the class and slot
amsfonts declares (provider Amsfonts, so \usepackage{amsfonts} alone
suffices, matching the .sty). \hbar needs a MANUAL entry: amsfonts puts
it on msbm "7E, the slot amssymb also gives \hslash, and unicode-math
names that character only as \hslash.

Since the original draft, main gained the package gate (543bde5) with
kernel-width arms for unloaded \angle/\hbar; those keep working and the
loaded case now takes the declared msam/msbm rows. \rightleftharpoons -
whose comment said "no row at all, so nothing to gate yet" - gets the
same treatment: a gated kernel arm (Rel, U+21CC, 1.000002em, the
measured 10.00002pt \mathpalette stack) so an unloaded document does
not regress to a missing-package error for a command base LaTeX2e does
define.

scripts/amssymb_api.rs.in is caught up with 84ff3c4's hand-edit of the
generated file (CharTable index instead of linear find), so
`python3 scripts/gen_amssymb.py > src/amssymb.rs` is byte-identical
again; verified before this change the generator reproduced main's
table except for that drift, and after it the diff is exactly the six
rows plus their LM advances.

Regenerated docs/user/compiler.md, supported/supported-latex.json,
supported/coverage.md and the Mac bundled copy via
scripts/render_supported_latex.sh.

Tests: tests/amssymb_skip.rs reconstructs pdfTeX's \show mathchars
("340A "045C "057E "3440 "3441 "0566) from the generated class, font
and slot, and pins the msam/msbm widths against the pdflatex \hbox
measurements. angle_and_hbar_keep_the_kernel_composite_without_amsfonts
extended to the loaded-case msam/msbm advances and to
\rightleftharpoons.

Salvaged from PR #212 (engine/amssymb-generator-skip), rebuilt against
current main (post-543bde5c package gating).

Implementation-Agent: fable-salvage
Commit-Executor: fable-salvage (direct; Cursor usage limit)

* compiler: regenerate supported-latex docs on the merged tree

docs/user/compiler.md (generated) conflicted with the #641 integration
batch; the merge took main's copy and this regenerates it via
scripts/render_supported_latex.sh on the merged tree, restoring the six
amssymb rows on top of main's new inventory (555 -> 559 math entries).
supported-latex.json and the Mac bundled copy were already correct;
sync-supported-latex.sh --check is byte-identical (704a3974fe88).
gen_amssymb.py re-verified byte-identical against src/amssymb.rs after
the merge.

Gates re-run on the merged tree: cargo test --release --no-run clean;
--lib 380 passed 0 failed; amssymb_skip 1 passed; supported_latex 8
passed; lm_math_binding 6 passed.

Implementation-Agent: fable-salvage
Commit-Executor: fable-salvage (direct; Cursor usage limit)

* integration fix: #579's borrowed fast path passes control_symbol_kern's post-#576 arguments

#579 predates #576 (kern control symbols measured by definition bytes);
its new parse_stream_body fast path called the old 3-argument form.
Pass maps_to_invocation and definition from the token entry, as the
slow path does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5Hmwh5GHNTiAmHUJ1MZu

* integration: regenerate supported-latex artefacts (render_supported_latex.sh; sync-supported-latex.sh)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5Hmwh5GHNTiAmHUJ1MZu

* integration fix: #579's fast path must not shadow main's tabbing control arm

#642 added a \\=/\\>/\\< tabbing arm to parse_stream_body's match that
runs before the control_symbol_kern arm. #579's borrowed fast path,
written before #642, matched those words first and typeset them as
text, so tabbing produced 0 tab stops instead of 2 (5 failures in
crates/compiler/tests/tabbing.rs). Mirror the slow path's guard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5Hmwh5GHNTiAmHUJ1MZu

* integration fix: perf-bench names #358's new diagnostics capability

main's crates/perf-bench (#206) constructs v1::Capabilities literally;
#358 adds the diagnostics field, so the bench stopped compiling (E0063)
and the engine performance job failed at Build. Pass diagnostics: false,
matching the bench's other opt-in capabilities.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5Hmwh5GHNTiAmHUJ1MZu

* integration fix: #294's page window and #358's diagnostics wire meet

#294 replaced Page's items field with PageContent + an accessor, added
DisplayList::{window, document_features}, and added tests/page_window.rs;
#358 adds Wire::diagnostics. Neither sees the other on its own branch:

- delta.rs/display.rs test helpers built Page and DisplayList literally
  (E0560, E0063 x2) -> Page::resident(..) and the two new fields as None
- tests/page_window.rs's const WIRE misses diagnostics (E0063)
- tests/math_symbols.rs took Page::items as a field (E0615) -- broken on
  main itself, fixed separately in #664 and carried here

cargo test --release --no-run on crates/render-pipeline: 0 errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5Hmwh5GHNTiAmHUJ1MZu

* integration fix: #579's fast path must not swallow an \obeylines newline

Third instance of the same combination bug, found by re-running the full
suite after the rebase rather than assuming it was mechanical.

#593 (landed on main via #649) makes a source newline end the line under
\obeylines: the lexer folds a lone newline into TokenKind::Space, and
parse_stream_body's Space arm turns it into an Inline::LineBreak. #579's
borrowed fast path, written before #593 existed, matched Space first and
skipped it, so crates/compiler/tests/obeylines.rs went 7 passing -> 2
passing / 5 failing.

Take the fast path for a Space only when \obeylines is not in force; a
Comment is still always skipped. Under \obeylines spaces fall through to
the slow path, which is where the newline is read from the token's own
source bytes.

cargo test --release --test obeylines: 7 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5Hmwh5GHNTiAmHUJ1MZu

---------

Co-authored-by: d-q222 <279808976+d-q222@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: muse-spark-1.3-contributor <muse-contributor@flashtex.invalid>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…e consumer

What changed:
main took #358's producer in via the integration commit 83285e3
("integration: compiler + pdf + v2 queue (#579, #640, #440, #621, #358)"),
so every Rust/protocol/docs/python file this branch carried is now already
on main byte-for-byte. `git diff MERGE_HEAD -- crates/ protocol/ docs/ tests/
tools/` is down to the one producer-side piece main does NOT have:
crates/document-runtime/src/lib.rs's capability allowlist.

Conflicts and how they were resolved:
- crates/render-pipeline/src/delta.rs, src/display.rs: took main. The branch's
  test helpers predate FT-070 (#294), which added `window` and
  `document_features` to DisplayList and turned `Page` into `Page::resident`.
  main's copies of `diag_list` / `header_only_diag_list` are the same tests
  with the new struct shape, and they pin the same Appendix A vectors
  (c4e7c712… on, e554935e… off).
- apps/mac/Sources/FlashTeXMac/DisplayListDelta.swift: both sides changed
  `installed(from:pageBytes:lineBytes:)`. Kept main's windowed-frame guard and
  its doc comment, and re-applied this branch's `diagnosticsCapability:` flag,
  which is what gates `suggestion` into the header digest.

Kept from this branch (nothing main provides):
- crates/document-runtime allowlist: `display-list-v2-diagnostics` accepted
  only alongside `display-list-v2`, with the pairing/unknown-cap tests.
- the whole Mac consumer: RenderingV2/RenderingV2Fast decode the optional
  `suggestion` (and tolerate labels/notes/help, which the proposal defines and
  main's producer documents as not yet on the wire), ShellModel.producerDiagnostics
  maps the live v2 frame through `asRuntimeV1` when compile_result.diagnostics
  is empty, and the delta digest hashes `suggestion` only when the capability
  was negotiated.

Also dropped a stray blank line the branch had added to EditorDiagnostics.swift.

What was run:
- CARGO_TARGET_DIR=/Users/dqi26/flashtex/target-merge363 CARGO_BUILD_JOBS=4
  cargo test --manifest-path crates/document-runtime/Cargo.toml
    lib:              test result: ok. 18 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out
    chunks:           test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
    display_sibling:  test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
    session:          test result: ok. 19 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
- apps/mac: swift build -> Build complete! (19.60 sec)
- apps/mac: swift test --filter 'RenderingV2|PreviewV2|EditorDiagnostics|ProblemsPanel|DisplayListDelta|V2Image'
    Executed 98 tests, with 12 tests skipped and 0 failures (0 unexpected) in 8.688 seconds

Next step:
push fast-forward to the PR branch and let CI run the full mac suite.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
…ers (unbreaks main)

What changed:
#689 (a7a90fc) added testPageReadoutCountsTheDisplayListNotTheElidedV1Pages
to PreviewV2ParityTests, but `model()` and `load(_:_:)` are private members of
PreviewV2ShellTests. The FlashTeXMacTests target therefore does not compile on
main, so the whole apps/mac suite is red:

  PreviewV2Tests.swift:807:25: error: use of local variable 'model' before its declaration
  PreviewV2Tests.swift:809:9: error: static member 'load' cannot be used on instance of type 'PreviewV2ParityTests'
  PreviewV2Tests.swift:809:13: error: argument passed to call that takes no arguments
  error: Build failed

The test is moved verbatim into PreviewV2ShellTests, which already has both
helpers and `Self.fixtures`, and is already @mainactor at class level. No
assertion changed. This merge cannot be validated at all without it.

What was run:
- apps/mac: swift build --build-tests -> Build complete! (12.09 sec)
- apps/mac: swift test --filter PreviewV2
    Test Suite 'PreviewV2ShellTests' passed
      Executed 9 tests, with 0 failures (0 unexpected) in 0.214 seconds
- Baseline check that the remaining apps/mac failures are main's, not this
  branch's: the same relocation applied to a detached origin/main worktree
  (dbe3cac) gives an identical failure set --
    origin/main:  Executed 52 tests, with 1 test skipped and 86 failures (0 unexpected)
    this branch:  Executed 52 tests, with 1 test skipped and 86 failures (0 unexpected)
  (swift test --filter 'SignatureHelpTests|SnippetTests|EditorChangeEnvironmentTests|EditorFirstFocusTests')

Next step:
the 86 SignatureHelp/Snippet/EditorChangeEnvironment/EditorFirstFocus failures
and the Completion-family abort are pre-existing on main and belong to another
lane; they are not touched here.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
@d-q222

d-q222 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

daniel-parent subagent — conflict resolved by merge (no rebase, no force-push). f0090a98..4cc23a95, now MERGEABLE.

What drifted

#358's producer already landed, not as #358 (which is CLOSED with no merge commit) but folded into 83285e36 integration: compiler + pdf + v2 queue (#579, #640, #440, #621, #358) (#658). So every Rust / protocol / docs / Python file this branch carried is now on main byte-for-byte:

$ git diff MERGE_HEAD --stat -- crates/ protocol/ docs/ tests/ tools/
 crates/document-runtime/src/lib.rs | 47 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

That one file is the only producer-side piece main does not have: the document-runtime allowlist that accepts display-list-v2-diagnostics only together with display-list-v2. Everything else on the Rust side was dropped in favour of main's copy.

Three conflicts:

  • crates/render-pipeline/src/delta.rs, src/display.rs — took main. This branch's test helpers (diag_list, header_only_diag_list) predate FT-070 (FT-070: a negotiated page window — the fix for a 500 KB document that has no reply #294), which added window / document_features to DisplayList and turned Page into Page::resident. main's copies are the same tests with the new struct shape, and they pin the same Appendix A vectors this branch's Swift goldens pin — c4e7c712… with the capability on, e554935e… with it off. No digest drift; the Swift golden tests needed no change.
  • apps/mac/Sources/FlashTeXMac/DisplayListDelta.swift — both sides changed installed(from:pageBytes:lineBytes:). Kept main's windowed-frame guard (guard list.window == nil) and its doc comment, and re-applied this branch's diagnosticsCapability: parameter, which is what gates suggestion into the header digest.

Also dropped a stray blank line this branch had added to EditorDiagnostics.swift.

The consumer field shape is unchanged by the drift. main's producer serialises suggestion on the v2 wire gated on Wire.diagnostics, omitted (never "") when empty — display::Diagnostic::wire_suggestion. That is exactly what RenderingV2/RenderingV2Fast decode here. labels/notes/help are decoded too; main's from_compiler documents that they are not on the v2 wire yet (folded into message/sources instead), so those decode paths are inert until the producer catches up — forward-compatible, not wrong.

#363 is not redundant. The Mac consumer half is entirely absent from main; only the Rust producer landed.

One thing that had to be fixed to test at all

main's apps/mac test target does not compile. #689 (a7a90fc8) added testPageReadoutCountsTheDisplayListNotTheElidedV1Pages to PreviewV2ParityTests, but model() and load(_:_:) are private to PreviewV2ShellTests. On a clean detached origin/main (dbe3cac8) worktree:

PreviewV2Tests.swift:807:25: error: use of local variable 'model' before its declaration
PreviewV2Tests.swift:809:9: error: static member 'load' cannot be used on instance of type 'PreviewV2ParityTests'
PreviewV2Tests.swift:809:13: error: argument passed to call that takes no arguments
error: Build failed

Commit 4cc23a95 moves that test verbatim into PreviewV2ShellTests (which owns both helpers and Self.fixtures, and is already @MainActor). No assertion changed. Happy for this to be split out if the integration lane would rather land it separately.

Test results

cargo test --manifest-path crates/document-runtime/Cargo.toml (CARGO_TARGET_DIR=…/target-merge363, CARGO_BUILD_JOBS=4):

lib:             test result: ok. 18 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 0.03s
chunks:          test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
display_sibling: test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.37s
session:         test result: ok. 19 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.37s
Doc-tests:       test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

apps/mac, as CI invokes it:

$ swift build
Build complete! (19.60 sec)
$ swift test --filter 'RenderingV2|PreviewV2|EditorDiagnostics|ProblemsPanel|DisplayListDelta|V2Image'
	 Executed 98 tests, with 12 tests skipped and 0 failures (0 unexpected) in 8.688 (8.693) seconds

Per-suite, the ones this PR owns:

Test Suite 'RenderingV2Tests' passed
	 Executed 19 tests, with 0 failures (0 unexpected) in 0.052 (0.053) seconds
Test Suite 'ProblemsPanelV2Tests' passed
	 Executed 4 tests, with 0 failures (0 unexpected) in 0.074 (0.074) seconds
Test Suite 'EditorDiagnosticsQuickFixTests' passed
	 Executed 6 tests, with 1 test skipped and 0 failures (0 unexpected) in 0.001 (0.001) seconds
Test Suite 'DisplayListDeltaTests' passed
	 Executed 4 tests, with 4 tests skipped and 0 failures (0 unexpected) in 0.001 (0.001) seconds
Test Suite 'PreviewV2ShellTests' passed
	 Executed 9 tests, with 0 failures (0 unexpected) in 0.214 (0.215) seconds

Broad run (the Completion family reliably aborts the whole FlashTeXMacTests process on this machine with Fatal error: Unexpectedly found nil — the known Mac Completion flake — so it is skipped to let the rest finish, along with the machine-dependent DesignSnapshots and the loopback Nearby*):

$ swift test --skip 'Completion' --skip DesignSnapshots --skip NearbyInterfaceTests --skip NearbyLANInterfaceTests
FlashTeXProtocolTests:       Executed 40 tests, with 0 failures (0 unexpected) in 1.031 (1.044) seconds
FlashTeXMacTests:            Executed 1252 tests, with 134 tests skipped and 86 failures (0 unexpected) in 184.893 (184.965) seconds
FlashTeXAccessibilityTests:  Executed 41 tests, with 1 test skipped and 0 failures (0 unexpected) in 1.341 (1.344) seconds

Those 86 are SignatureHelpTests (61), SnippetTests (20), EditorChangeEnvironmentTests (3), EditorFirstFocusTests (1) — all variants of "\dfrac is not in the compiler inventory" / "expected non-nil value of type Snippet", i.e. the bundled completion vocabulary not reaching the test bundle. They are main's, not this branch's. Same four classes, same numbers, on a detached origin/main worktree with only the PreviewV2Tests relocation applied so it would compile:

origin/main (dbe3cac8):  Executed 52 tests, with 1 test skipped and 86 failures (0 unexpected) in 47.835 (47.838) seconds
this branch (4cc23a95):  Executed 52 tests, with 1 test skipped and 86 failures (0 unexpected) in 47.201 (47.204) seconds

(apps/mac/scripts/sync-supported-latex.sh --check — the CI gate for that resource — passes: bundled copy is current (2e9dff55b6e4).)

Not done

  • The 86 inventory-resource failures and the Completion-family abort are pre-existing main breakage and are left alone.
  • labels / notes / help over the v2 wire still wait on the producer.
  • iOS consumer.

🤖 Generated with Claude Code

https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

@d-q222

d-q222 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Heads-up from daniel-parent: the PreviewV2 test move in 4cc23a95 duplicates PR #698.

While this lane was validating the merge it hit main's apps/mac build break (#689 put testPageReadoutCountsTheDisplayListNotTheElidedV1Pages in PreviewV2ParityTests, whose helpers are private to PreviewV2ShellTests) and moved the test so it could build at all. That was the right call locally — the merge could not be validated otherwise — but a parallel lane had already opened #698 for exactly that fix, tracked as #695.

Integration lane: prefer #698 for the unbreak, since it is single-purpose and its body carries the before/after build output. If #698 lands first, this branch's copy of the move should drop out as an identical change or resolve trivially; if it does not resolve cleanly, take main's side. I am deliberately not rewriting history here to remove it — this branch is already pushed and reviewed, and no force-pushes.

Everything else in this PR is its own: after merging main the Rust/protocol/docs diff collapsed to one file, +47 lines (the crates/document-runtime allowlist accepting display-list-v2-diagnostics alongside display-list-v2), because #358's producer landed folded into 83285e36. The Mac consumer half is still missing from main, which is what this PR is for.

— daniel-parent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants