Migrate RenderedChannelParticipant.peers from [PeerId: Peer] to
[EnginePeer.Id: EnginePeer]. Closes the wave-41 ratchet — the public
struct no longer leaks raw Peer types in any field (presences stays
Postbox-typed; separate migration).
Consumer-surface: -10 bridges. Dropped 6 EnginePeer(peer) read-wraps
at participant.peers[...] extraction sites across
ChannelAdminsController, ChannelMembersSearchContainerNode,
ChannelBlacklistController. Dropped 5 .mapValues({ $0._asPeer() })
constructor-unwrap transforms in ChannelAdminsController,
ChannelMembersSearchContainerNode, ChannelMembersSearchControllerNode.
Added 2 ._asPeer() unwraps in ChatRecentActionsHistoryTransition at
the two iteration sites (line 673 via participant.peers, line 2273
via new.peers in participantSubscriptionExtended) where the iterated
value is inserted into a raw-Peer SimpleDictionary.
TelegramCore producers: 8 files build the local peers dict inside
postbox.transaction and wrap at the insertion point. ChannelMembers,
RequestStartBot, ChannelOwnershipTransfer, JoinChannel, AddPeerMember,
PeerAdmins, ChannelBlacklist, Ranks.
2-iteration build convergence. Iteration-1 surfaced new.peers at
ChatRecentActionsHistoryTransition:2272 that the plan's participant.peers
pre-flight grep missed; wider grep now confirms the two iteration sites
are the complete surface.
No unit tests in this project; full Telegram/Telegram build verified
under configuration=debug_sim_arm64.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The bubble-in-text-input morph ran concurrently with listNode's
scroll-to-item sublayerTransform animation, but its source rect was
computed via CALayer.convert which uses model transforms only and so
missed the pending additive sublayerTransform. The morph's `from`
ended up calibrated to the post-scroll state while playback started
from the pre-scroll state, offsetting the bubble at t=0.
Add convertAnimatingSourceRectFromWindow: walks the layer chain
top-down and at each parent->child step subtracts the parent's pending
additive sublayerTransform translation in the parent's bounds space
before descending, so the result reflects the t=0 presentation state
even through rotations/scales later in the chain. Parent
animatingItemNode under itemNode so listNode is on its ancestor chain
and its pending sublayerTransform is picked up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Private free function in ContactMultiselectionController.swift. Five
_asPeer() bridges drop in one atomic single-file commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trims the Postbox-refactor section of CLAUDE.md from ~750 lines to ~160
by moving per-wave outcomes, the running tally of Postbox-free modules,
and verbose wave-selection guidance backstory into
docs/superpowers/postbox-refactor-log.md. Also adds a spec document for
the reorganization.
Kept in-place in CLAUDE.md: standing rules, engine typealias cheat sheet,
MediaResource consumer-migration patterns, TelegramEngine.Resources facade
inventory, and terse bullet-form guidance with log cross-references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task-by-task implementation plan for the caret-tracking auto-scroll
design. Single-file change in TextStyleEditScreen.swift: tag both text
fields, walk the transition's TextFieldComponent.AnimationHint userData
on .textChanged, compute caret rect, and adjust the enclosing
ResizableSheetComponent scroll view's bounds.
Design for auto-scrolling the ResizableSheetComponent scroll view on text
change so the caret in either ListMultilineTextFieldItemComponent stays
visible above the soft keyboard.
Mechanical regeneration after the SwiftTL fix. Every
'if Int(_N!) & Int(1 << K) != 0 {...}' gate and matching
'_cM = (Int(_N!) & ...) == 0) || ...' validation in parse_<ctor>
now reads 'Int(_N ?? 0)' instead of force-unwrapping. ~2168 sites
changed across the 41 flat Api*.swift files plus the 11 layered
SecretApiLayer*.swift files. No other content change.
Full Bazel build verified green with the regenerated sources.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generated parse_<ctor>(...) for constructors with conditional fields
used to emit 'if Int(_N!) & Int(1 << K) != 0 { ... }' as the gate,
and 'let _cM = (Int(_N!) & Int(1 << K) == 0) || _M != nil' as the
per-field validation. Both force-unwrap _N (the flags field read)
before the bottom-of-function '_cN = _N != nil' validation runs,
so a buffer short enough to fail the flags read traps
deterministically instead of returning nil.
Replace the force-unwraps with (_N ?? 0). Missing flags then reads
as "all bits off": every gated branch is skipped, each flag-gated
_cM short-circuits to true via the '== 0' clause, and the flags
field's own _cN = _N != nil still fails so the overall constructor
validation falls through to return nil — matching the intended
"return nil on truncated buffer" contract.
Touches both generator emit paths (flat generateImplFile and layered
emitLayeredType). Regenerated Api*/SecretApiLayer*.swift follow in a
separate commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the hand-written layer 8/46/73/101/144 files with SwiftTL
output and adds the previously-unpublished layers 17/20/23/45/66/143.
Per-layer struct names (SecretApi8, SecretApi46, ...) and public
enum case signatures are unchanged for the consumers.
The regenerated cumulative snapshots now include every constructor
declared in secret_scheme.tl, including decryptedMessageActionTyping
(layer 17) which the hand-written files had elided. Switch statements
over DecryptedMessageAction in ProcessSecretChatIncomingDecryptedOperations
gain the missing case alongside .decryptedMessageActionNoop (both are
no-ops in the consumer-side state-machine).
BUILD uses glob("Sources/**/*.swift") so no BUILD update required.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the actual build invocation (with codesigning + cache +
configuration arguments) at the top of the file, alongside the
existing notes that only Bazel + the codesigning env var were
mentioned. Removes the duplicate "Build environment quirk" footer
section since its content now lives at the top.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Flat schemas keep the existing generate(...) pipeline. Layered
schemas iterate resolveLayeredTypes and write one
{apiPrefix}Layer{N}.swift per layer via generateLayered.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>