Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14 KiB
Phase 0 oracle registry — RichText input backend seam
Every Phase-4 routing task cites the gate for its category from this table instead of
re-deriving one. A test in T/Characterization/OracleRegistryTests.swift fails if a
named class disappears, so a rename cannot silently drop a gate.
Every count below is a mechanical grep -c 'func test' <file> on the file as it stands on
richtext/input-backend-seam at the time this manifest was written (Task 8). Where a file
covers more than one category, it is listed (and counted) under each — that is intentional
double-filing, not double-counting toward any one category's total.
| Category | Gate classes | Measured test count |
|---|---|---|
| a. text/range primitives | CanvasTextInputTests (5), TextPositionTests (2), CanvasTokenizerTests (8), CaretRenderableTests (7), TextInputWitnessMatrixTests (53, Task 4) |
75 |
| b. directional selection | BaseDirectionTests (4), RTLCaretTrackingTests (4), DirectionalSelectionCharacterizationTests (3, Task 4) |
11 |
| c. delegate/facade order | EditingInputDelegateBracketTests (4), SelectionDragCoalescingTests (3), DelegateTraceCharacterizationTests (23, Task 3) + FacadeCallbackTraceCharacterizationTests (4, Task 3) |
34 |
| d. undo coalescing | UndoCoalescingTests (14), UndoBufferIsolationTests (7) |
21 |
| e. Return/Backspace boundaries | DoubleReturnExitTests (34), BlockQuoteEditTests (52), CanvasPullQuoteEditTests (34), CodeBlockEditingTests (14), DetailsBoxReturnTests (2), CanvasTableCellBackspaceTests (1), CanvasTableBackspaceDeleteTests (7), CanvasTableBackspaceSelectTests (7), CanvasStructuralTests (13) |
164 |
| f. marked text | MarkedTextTests (27), MarkedTextTraceCharacterizationTests (10, Task 7) |
37 |
| g. autocorrect/prediction | AutocorrectUnderlineTests (10), NativeTextCheckingClientTests (10), AutocorrectOriginCharacterizationTests (3, Task 7) |
23 |
| h. geometry | ComposerSelectionGeometryTests (11), SelectionHighlightTests (10), CanvasHitTestTests (2), CanvasEditMenuPositionTests (4), GeometryWitnessMatrixTests (23, Task 5) + TableScrollGeometryCharacterizationTests (5, Task 5) |
55 |
| i. touch/loupe/handles/menu | SelectionInteractionTests (26), TableControlsTests (57), CanvasSelectionMenuTests (32), CanvasHitTestTests (2, shared with h), EditMenuAutoDismissTests (8), CanvasEditMenuPositionTests (4, shared with h) |
129 |
| j. floating cursor/autoscroll | FloatingCursorTests (17), TransientCaretViewTests (3), ScrollCaretOnEditTests (7) |
27 |
| k. clipboard/structural | CanvasClipboardTests (24), RTFConversionTests (24), CanvasCellStructuralTests (4), CanvasSelectAllTableDeleteTests (6), CanvasReplaceRangeTests (6) |
64 |
| l. responder/window detach | ResponderLifecycleCharacterizationTests (15, Task 6) + WindowDetachCharacterizationTests (5, Task 6), RichTextEditorViewTests (27) |
47 |
| m. spelling annotations | SpellCheckTapTests (15), SelectionDrivenSpellCheckTests (9), NativeTextCheckingClientTests (10, shared with g), NativeTextCheckingResolverTests (2), NativeTextCheckingLiveTests (1), AutocorrectUnderlineTests (10, shared with g) |
47 |
Discrepancies found against the brief's hypothesis (Task 8)
The brief's file lists for categories d, e, i, j, k, m were re-derived against what is actually on disk. Every file in every one of those six rows was opened and read in full (not just its function names). Two corrections came out of that:
-
Category e (Return/Backspace boundaries):
DetailsBodyEditTestsdoes NOT belong here and has been dropped from the gate-class list. Reading the file (T/DetailsBodyEditTests.swift, 8 tests) shows every test exercises a format/insert command (setParagraphStyle,setList,insertTable,insertDetailsBlock,insertMedia,makeCodeBlock,makePullQuote,wrapInBlockQuote) landing inside a details body rather than at the top level — i.e. it is a container-routing regression suite, not a Return/Backspace boundary suite. It asserts nothing about Enter or Backspace. The genuine Return-boundary gate for details isDetailsBoxReturnTestsalone (2 tests: single- vs. double-Return escaping the details body), which remains in the list. -
Category e (Return/Backspace boundaries):
CanvasReplaceRangeTests(6 tests) does NOT belong here either — it has been re-homed to category k (clipboard/structural). The original filing was justified as "the cross-block range-replace engine Backspace/paste ultimately route through," which is checkable and false. Tracing the call graph (grep -noverSources/RichTextEditorUIKit/Canvas/):deleteBackward()callsapplyReplaceOutcome(globalFrom:globalTo:text:)(DocumentCanvasView+UITextInput.swift:686,883,904) — a different function, defined atDocumentCanvasView+Editing.swift:93. Backspace never reachesreplaceRange.- Ordinary paste (fragment/RTF/plain) calls
applySelectionReplaceOutcome(DocumentCanvasView+Clipboard.swift:122,150) — also notreplaceRange. replaceRange(globalFrom:globalTo:with:)(DocumentCanvasView+Editing.swift:841) is called from exactly two places: the public façadeRichTextEditorView.replaceRange(RichTextEditorView.swift:509-510), andpasteMarkdownTwoStep's step 2 (DocumentCanvasView+Clipboard.swift:178) — the host-injected markdown-on-paste hook, a narrow special case, not paste generally.
So
CanvasReplaceRangeTestsgenuinely tests the façade-level / markdown-paste-injectedreplaceRangeAPI, not a Return/Backspace boundary. Its actual content — replacing an arbitrary global range with aDocumentfragment, including the whole-table-drop behavior on both orientations of a mixed range and the one-undo-step guarantee — is cross-block structural replacement, which is what category k's "clipboard/structural" name covers (the API is also literally reached from the clipboard/paste path above), so it was re-homed there rather than dropped outright.
Both corrections drop category e's total from the brief's implied 178 down to the verified
164 (removing DetailsBodyEditTests's 8 and CanvasReplaceRangeTests's 6), and raise
category k's total from the brief's implied 58 up to 64 (adding CanvasReplaceRangeTests
back in under its correct category).
Every other file named in the brief for categories d, e, i, j, k, m was opened and
confirmed to assert what its category claims; every measured count above matches the
brief's prediction exactly except the two rows corrected above:
UndoCoalescingTests 14, UndoBufferIsolationTests 7, DoubleReturnExitTests 34,
BlockQuoteEditTests 52, CanvasPullQuoteEditTests 34, CodeBlockEditingTests 14,
DetailsBoxReturnTests 2, CanvasTableCellBackspaceTests 1,
CanvasTableBackspaceDeleteTests 7, CanvasTableBackspaceSelectTests 7,
CanvasStructuralTests 13, SelectionInteractionTests 26, TableControlsTests 57,
CanvasSelectionMenuTests 32, CanvasHitTestTests 2, EditMenuAutoDismissTests 8,
CanvasEditMenuPositionTests 4, FloatingCursorTests 17, TransientCaretViewTests 3,
ScrollCaretOnEditTests 7, CanvasClipboardTests 24, RTFConversionTests 24,
CanvasCellStructuralTests 4, CanvasSelectAllTableDeleteTests 6, SpellCheckTapTests 15,
SelectionDrivenSpellCheckTests 9, NativeTextCheckingClientTests 10,
NativeTextCheckingResolverTests 2, NativeTextCheckingLiveTests 1,
AutocorrectUnderlineTests 10.
Mixed-coverage disclosure — BlockQuoteEditTests / CanvasPullQuoteEditTests (category e)
Both files are legitimately filed under category e — each contains a large, genuine block
of Return/Backspace-at-quote-boundary tests (roughly half of each file: double-return exit
at the beginning/middle/end of the quote body and author, backspace un-quoting/outdenting/
merging at every child position, Select-All+Backspace resets, the resolveBox-misroute
regression suite). But each file's remaining tests exercise other categories, read in
full rather than assumed:
BlockQuoteEditTests(52 tests) also covers: format-command no-ops/toggles (test_wrapInBlockQuote_wrapsTwoParagraphs,…nestsWhenAlreadyInsideQuote,test_unwrapBlockQuoteLevel_*×2,test_toggleBold_isNoOp_inAuthorRegion,test_wrapInBlockQuote_insideTableCell_isNoOp); insert-command no-ops (test_insertTable_caretInQuote_isNoOp_followingBlockUntouched,test_insertMedia_caretInQuote_isNoOp_followingBlockUntouched); vertical/arrow navigation (test_verticalNav_*×3,test_arrowRight_intoNonEmptyBlockQuoteAuthor_…); Tab navigation (test_tab_*×4); tap placement (test_tapInBlockQuoteAuthorArea_placesCaretInAuthor); and toolbar/state queries (test_currentState_caretInQuote_…,test_editorState_blockQuoteDepth). These touch categories a (text/range), b (directional/vertical nav), h (geometry/tap), and i (touch placement) — not e.CanvasPullQuoteEditTests(34 tests) has the same pattern: a format-toggle command (test_makePullQuote_togglesParagraphsIntoOneBlock_preservingFormatting); author-region insert/replace routing that is plain text insertion, not Return/Backspace (test_insertText_atEmptyPullQuoteAuthor_*×2,test_replace_atPullQuoteAuthor_…,test_selectionReplace_viaInsertText_atPullQuoteAuthor_…); typing-attribute checks (test_pullQuote_emptyTypingAttributesAreItalic,…AreCentered,test_pullQuote_typingAttributesAtGlobal_returnsItalicWhenEmpty); arrow nav and tap placement (test_arrowRight_intoNonEmptyPullQuoteAuthor_…,test_tapInPullQuoteAuthorArea_*×2); composer flat-range primitives (test_composerFlatRange_*×2,test_composerSelectedRange_caretInsidePullQuote_…); and framed-neighbor spacing geometry (test_pullQuoteNeighbors_reserveExtraExternalMargin). These touch categories a, g (autocorrect/prediction-adjacent typing attributes), h, and i.
Neither file is marked PARTIAL for category e — the Return/Backspace-boundary half of each is on its own an ample, on-target gate — but a Phase-4 task citing either file for anything beyond category e's boundary behavior should verify the specific test it wants, not assume the whole file is in scope.
Known gap — not coverable by any test (category i)
Two of deviation D18's four survivals cannot be pinned by any test today, because no seam
exists to drive the private long-press handler without a Sources/ change:
- The loupe session lifecycle (creating/tearing down the per-drag
UITextLoupeSession). - The per-drag
UITextSelectionDisplayInteractionthathandleLongPresscreates fresh and removes on release (seePKG/CLAUDE.md, "Loupe grow-from-cursor + gliding shadow caret cursor").
Both are confirmed correct by code reading only (DocumentCanvasView+FloatingCursor.swift
/ the long-press handler), not by any executable test. A Phase-4 task routing category i
must treat this as an accepted, pre-existing hole — not something this registry's tests can
promote to "covered."
TextKit 1 skip census
Filled in by Task 9. The only legitimate skip reasons are the three documented TK1 trade-offs in
PKG/CLAUDE.md: no spoiler text-hiding, no loupe, no inline predictions.
Empirical result: zero new skipOnTextKit1 call sites were needed. The full suite
(Scripts/iostest.sh, 1800 tests as of this task — 1797 pre-existing + 3 new EngineSkipTests)
passes with 0 failures under a genuinely forced TK1 pass (TK1=1 Scripts/iostest.sh,
verified to actually flip BlockLayoutBackend.forceTextKit1 — see the environment-forwarding
finding below). The only difference between the unforced and forced runs is one additional
skip (5 → 6): SpoilerReconcileTests.test_reveal_removesHide_andBumpsRenderVersion_soTextRepaints,
which already carried a pre-existing, Task-9-independent XCTSkip keyed on
textLayout as? BlockLayout returning nil (SpoilerReconcileTests.swift:37, reason: "spoiler-hide
display is TextKit-2 only (disabled on the TK1 back-port)") — one instance of the documented
"no spoiler text-hiding" trade-off, caught by a type-check rather than the new
isForcedTextKit1/skipOnTextKit1 helper this task adds.
Census reconciliation. The census (written at Task 8, before the mechanism existed) hypothesized
that the suite would need new engine-conditional skips across all three documented trade-offs. That
hypothesis was wrong in scope, not in kind: the three trade-offs are real (see PKG/CLAUDE.md),
but none of the "loupe" or "inline predictions" trade-offs have a unit test that exercises the
gap — the loupe is created/torn down by handleLongPress (already recorded as untestable without a
Sources/ change, see "Known gap" above) and the inline-prediction ghost/dismiss logic is exercised
only through paths that don't depend on which BlockLayoutEngine is active. So there was nothing for
skipOnTextKit1 to guard in the current suite; the helper exists for Phase 4+ tests that do
reach one of these gaps (its own EngineSkipTests are the only current call site, and those exist to
test the helper itself, not to skip a real trade-off). This is recorded here rather than fabricating
speculative skip sites — a skipOnTextKit1 call with no corresponding failure would be dead code
masking nothing.
Load-bearing environment-forwarding finding (verified empirically, not from the plan template).
TEST_RUNNER_RTE_FORCE_TK1=1 must be a genuine shell/process environment variable exported before
invoking xcodebuild, not a trailing xcodebuild test ... TEST_RUNNER_RTE_FORCE_TK1=1 command-line
argument. Both forms were tried directly against this SwiftPM scheme; a diagnostic probe dumping
ProcessInfo.processInfo.environment inside the test process showed the trailing-argument form never
reaches the test runner (confirmed with three orderings and a generic TEST_RUNNER_FOO=bar control),
while exporting it first (or prefixing the invocation, e.g. TEST_RUNNER_FOO=bar xcodebuild test ...) does. Scripts/iostest.sh uses export for this reason — see the comment at its TEST_RUNNER_
line. Had this not been caught, every "TK1-forced" run in this project would have silently run under
TK2 the whole time, and the double-run mechanism this task builds would have been non-functional
while appearing to pass.