Conversation
Phase 0: mise Python 3.12 + venv, keynote-parser 1.14.4.0 (pinned), mise tasks, and toolchain validation against Keynote 15.3 (unpack works; pack does not round-trip due to the 15.3-vs-14.4 mapping gap — documented, not blocking). Phase 1 (transitions, fully scripted): transition attaches to the slide archive's animationAttributes; effect is a string enum matching the sdef Cocoa values; duration/delay are verbatim seconds; auto-advance is just isAutomatic; and Magic Move object correspondence is NOT persisted (runtime heuristic), so magic-id must be a compile-time abstraction (confirmed via shapes and duplicate-and-edit). Adds normalizer object-id canonicalization, transition generators, Phase 2 builds base/control fixtures + steps, a Deck model synthesis, and HANDOFF.md. Co-authored-by: Cursor <cursoragent@cursor.com>
Built one deck per representative effect (dissolve/wipe/move-in/iris/object-cube/ object-flip/switch) and extracted each transition block. All effect strings match the sdef Cocoa values. Effect-specific knobs are sparse custom* siblings of animationAttributes: only object flip emits customBounce; magic move emits its three; the rest emit none. No direction is serialized at default. Updates effect_type.md, deck_model_notes.md (transition options = extensible per-effect bag), and HANDOFF.md. Adds effect_matrix + one_effect generators. Co-authored-by: Cursor <cursoragent@cursor.com>
A 3-slide deck with a different effect per slide stores each effect in its own slide archive (no document-level list, no coupling), and slide 1 carries a transition like any other slide. Confirms Transition is a per-Slide field. Adds multi_slide generator + findings; updates deck_model_notes. Co-authored-by: Cursor <cursoragent@cursor.com>
Implements the scriptable half of the backend end-to-end: - tools/deckkit.py: Deck/Slide/TextItem/Transition IR, JSON spec loader, an EFFECTS map (name -> archive string + AppleScript enumerator), AppleScript code-gen that drives Keynote to build the .key (per-slide transitions incl. slide 1), and a round-trip verifier. - tools/build_deck.py: CLI (build + optional --verify via keynote-parser unpack). - examples/deck_example.json: 4-slide sample. Verified end-to-end (VERIFY PASS). No pack dependency. Builds/direction (non-scriptable) remain future work. Updates deck_model_notes.md and HANDOFF.md. Co-authored-by: Cursor <cursoragent@cursor.com>
Untrack the accidentally-committed compiled module and add __pycache__/ + *.pyc to .gitignore. Co-authored-by: Cursor <cursoragent@cursor.com>
tools/test_deckkit.py exercises spec parsing, AppleScript code-gen (terms, quote-escaping, per-slide transition count), and the verify/extract logic on a synthetic unpacked deck (positive + negative). Adds mise tasks `test` and `build-example`. Both `mise run test` and `mise run selftest` pass. Co-authored-by: Cursor <cursoragent@cursor.com>
Extends deckkit.EFFECTS to the complete Keynote 15.3 transition enum (43 effects + none) and verifies every archive effect string round-trips by building a 43-slide catalog deck (examples/effect_catalog.json) and running build_deck --verify (43/43 PASS). Findings from the sweep: - radial wipe's archive string is `apple:radial wipe` (contains a space; the raw sdef value is misleading). Fixed the verifier's effect regex to accept spaces. - Per-effect custom* options catalogued: customBounce (bool) on object flip/ object revolve/cube/flip/scale/revolving door; customTravelDistance (fade and move); customTwist (twist); magic move's three. All others emit none, and none are AppleScript-settable. Updates effect_type.md, deck_model_notes.md, HANDOFF.md, and extends the no-Keynote unit test (EFFECTS completeness + space-in-effect regression). Co-authored-by: Cursor <cursoragent@cursor.com>
Human-authored golden pairs for build-in vs none, build order, and build effect/timing sweeps, ready for differential diffing. Co-authored-by: Cursor <cursoragent@cursor.com>
Diff the hand-authored build fixtures (build_in/order/fx) and document how
Keynote's .key stores object builds:
- A build = KN.BuildArchive (effect in a transition-shaped animationAttributes,
targeting its object by drawable.identifier) + KN.BuildChunkArchive (timing),
referenced from new builds/buildChunks lists on KN.SlideArchive. Document
flips hasExplicitBuilds -> true (derived cache).
- Order is list position; no explicit order field (Exp 6).
- Build effects: apple:dissolve character / apple:move in character (distinct
from transition strings). Move In carries animationAttributes.direction (int)
-- the same field transitions omit at default.
deckkit.py: add the Build IR (builds on Slide), BUILD_EFFECTS, spec loading,
and the read/verify half (extract_builds/verify_builds). No build write path --
builds are not AppleScript-scriptable (byte-surgery, future). extract_transitions
now filters by animationType so builds aren't miscounted; transition round-trip
still VERIFY PASS.
findings: build_in.md (Exp 5), build_order.md (Exp 6), build_fx.md (Exp 7);
deck_model_notes.md gains a Build {} section; HANDOFF.md marks Phase 2 done.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Scaffold per-repo config for the Matt Pocock engineering skills: - AGENTS.md with an "Agent skills" section - docs/agents/issue-tracker.md — GitHub issues via gh CLI - docs/agents/domain.md — single-context domain-doc consumer rules Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the append-only corrections log and its loader instruction from global ~/.claude into the repo so they are versioned and committed: - CORRECTIONS.md at the repo root (append-only source of truth) - AGENTS.md gains a "Corrections log" instruction pointing at it Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Relocate CORRECTIONS.md from the repo root to .claude/ (preserves history via git mv); update the AGENTS.md pointer to .claude/CORRECTIONS.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stage the next human-in-the-loop fixtures for features Keynote won't script: - findings/builds_setup_2.md — per-experiment steps for Exp 8 (non-text/shape build, settles the "character"-suffix question), Exp 9 (build-effect catalog), Exp 10 (Build Out / Action), Exp 11 (transition direction). - generators/base_shape_build.applescript — base with one shape (verified: emits a Shape archive, no builds). - generators/direction_base.applescript — 2-slide deck with a scripted Move In transition at default direction (verified: slide 2 = apple:slide, Transition). Downstream diff/analysis stays unattended once the fixtures are saved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add dependency-free unit tests (synthetic unpacked decks, no Keynote/keynote-parser): - test_parse_builds / test_unknown_build_effect_and_kind — Build spec loading + validation - test_build_effects_map — BUILD_EFFECTS archive strings; no AppleScript term - test_extract_and_verify_builds — extract_builds fields (incl. direction/drawable) + verify_builds positive/negative - test_extract_transitions_excludes_builds — the animationType filter routes Transition vs In blocks correctly mise run test -> DECKKIT TEST PASS (11 tests); mise run selftest -> SELFTEST PASS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
findings/pack_mappings.md: bounded spike on regenerating keynote-parser's 15.3 pack mappings. The proto-definition half runs unattended today (33 15.3 protos extracted via the source's pure-Python protodump.py — no C++ proto-dump needed — and compiled with protoc; TSKArchives.sos dropped vs 14.4, proving a real schema shift). Blocked on the TSPRegistryMapping (archive-type-ID -> message-name), which is a runtime LLDB dump of [TSPRegistry sharedRegistry] and needs human/toolchain setup (Homebrew LLVM w/ matching Python + debuggable re-sign of the sandboxed MAS app). Nothing installed/committed beyond the note; experiments stayed in scratch. HANDOFF.md: point build follow-ups at builds_setup_2.md, note the unit tests, and record the pack-spike outcome; refresh the findings index + generator list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Decision-support note (companion to pack_mappings.md/versions.md) explaining why a byte-surgery backend is needed for builds/direction, how pack works and why it's broken for 15.3, the TSPRegistryMapping blocker, and three options (A: reuse the 14.4 registry + round-trip test — cheap/mostly unattended; B: full LLDB runtime dump — heavy, needs SIP/re-sign; C: defer) with an at-a-glance comparison. Includes a full glossary of the IWA/protobuf/codesign/LLDB terminology. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prep tooling for the incoming build experiments (esp. the Exp 9 effect catalog): - deckkit.extract_builds now also returns an `options` bag per build (customBounce / customTravelDistance / customTextDelivery / customDeliveryOption / delivery / eventTrigger), the sparse bag Build.options models; scalars coerced (bool/int/float/str), space-containing `delivery` preserved, BuildArchive.duration excluded. - tools/read_builds.py: CLI that unpacks one+ .key files and prints their builds (kind/effect/timing/direction/drawable + options), --json supported. - mise task `read-builds`. - test_extract_build_options covers the options capture. Verified against committed fixtures: build_fx_B -> apple:move in character, direction 13, customBounce, delivery By Paragraph; build_in_B -> the dissolve build. mise run test -> DECKKIT TEST PASS (12); selftest PASS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Append two entries to the append-only corrections log. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the completed .key fixtures for builds_setup_2.md Exps 8-11: - Exp 8: build_shape_A/B (shape Dissolve build) - Exp 9: build_cat control + 9 Build-In effect variants (appear, dissolve, movein=Fly In, fadein=Fade and Scale, fademove, scale, shiftscale=Shift and Scale, blur, flip) - Exp 10: build_out_A/B, build_action_B - Exp 11: direction_A/B Also updates the Exp 9 effect table to match the actual Keynote Build In menu (Fly In, Fade and Scale, added Shift and Scale row). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 2b experiments (fixtures authored by hand, analyzed unattended): - Exp 8 (build_shape): the " character" effect suffix is NOT object-type-qualified — a shape's Dissolve = apple:dissolve character too. - Exp 9 (build_catalog): 8-effect catalog across three naming families; direction/customBounce/customTravelDistance options captured. - Exp 10 (build_out): In/Out/Action are one animationType enum on the same KN.BuildArchive; Action is a distinct payload (motion path, no text delivery). - Exp 11 (direction): transition direction = animationAttributes.direction (int), the same slot builds use. Option A (pack write-backend): ACCEPTED. Pairing the installed 14.4 TSPRegistryMapping with regenerated 15.3 protos packs a .key Keynote 15.3 opens (regression_check.key, 4 slides). Type IDs are stable 14.4->15.3, so the LLDB runtime dump (Option B) is not needed. See findings/pack_option_a.md. Rolled findings into deck_model_notes.md + HANDOFF.md; marked pack_backend_approach.md section 5 RESOLVED. Also mirror the auto-memory set into .claude/memory/ so memories are version-controlled with the repo. Raw findings/<exp>/ diff dirs left untracked, per existing convention (regenerable). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Authored decks containing any object build crashed Keynote 15.3 with EXC_BREAKPOINT (SIGTRAP) in -[__NSSetM addObject:]. A 4-deck bisect (examples/bisect_*.json) isolated it: a direction-only deck reopens, while one Build In, one Build Out, and one Action each crash — so the pack path, write_back surgery, and hybrid parser were all sound. The decoded slide YAML was equivalent to the human-authored fixtures throughout; the missing invariants were document-level, in Metadata.iwa.yaml: 1. Every KN.BuildArchive id must be registered in TSP.PackageMetadata -> the slide's component -> objectUuidMapEntries, with the entry's uuid equal to the KN.BuildChunkArchive's buildId. Measured 8/8 human-authored builds registered, 0/2 of ours; chunk ids are registered in 0/8. 2. lastObjectIdentifier must stay strictly above every minted archive id. 5/5 fixtures hold this; ours inverted it. Both fit the crash frame — a set insertion failing on an identity Keynote cannot resolve while rebuilding its animation registry on load — and both are absent from the direction-only deck, which allocates no ids. archive_backend now registers one entry per emitted build and bumps the high-water mark only when ids were allocated, leaving the working direction-only path untouched. _verify_uuid_map enforces both from inside write_back, since deckkit.extract_builds reads only Index/Slide*.iwa.yaml and structurally cannot observe them. Human reopening confirms all four bisect decks and the combined acceptance deck now open with no crash and no repair warning: newly authored In, Out, Action, multiple ordered builds, and transition direction all survive. Also generalizes authored_build_smoke.py to take spec/output arguments with a spec-derived reopen assertion (no-arg behavior unchanged), and adds --no-reopen for generating diagnostic decks without driving Keynote. Tests: 18 pass (4 new, over a synthetic unpacked tree); both fixes were mutation-tested. mise run test / selftest / verify-pack all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Frees the repo root for Package.swift, Sources/, and Tests/ by relocating all reverse-engineering content into research/: tools/, findings/, fixtures/, generators/, examples/, vendor/, plus the gitignored working directories samples/, unpacked/, and build/. Pure relocation — git detects all 282 files as renames with no content change. The Python tools resolve their paths from __file__ via Path(...).parents[1], so moving the tree as a unit keeps every internal reference correct with no code change. Updated: - mise.toml task paths, including the two tasks passing relative paths as arguments (build-example, transitions) - .gitignore, anchoring research/samples/, research/unpacked/, and research/build/keynote-parser/ (the last was root-anchored and would otherwise have stopped matching) - README.md layout section and tool references Verified after the move: the hybrid parser cache rebuilds from scratch to the identical hash (86ee8d81), confirming the vendored schema inputs are byte-identical; mise run test (18) / selftest / verify-pack all pass; and end-to-end authoring regenerates a build deck through the full unpack -> author -> pack -> verify path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lls. Consolidate standing directives into a supersedable in-repo log and document the convention in AGENTS.md so future sessions read one source of truth. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the full
keynote-format-labprototype onto thev0.1.xrelease line: 27 commits reverse-engineering how Keynote's.keyformat represents slide transitions and object builds, plus a working write backend.What's here
Phase 0 — toolchain. Validated
unpack, and fixedpack, which the stock keynote-parser could not do for Keynote 15.3. Option A pairs the installed 14.4TSPRegistryMappingwith regenerated 15.3 protos; type IDs proved stable, so the LLDB runtime dump was never needed. Vendored as a reproducible schema (mise run prepare-keynote-parser, 631/631 registry entries).Phase 1 — transitions. Established that transitions live at
KN.SlideArchive.transition.attributes.animationAttributes;effect: nonemeans off. Catalogued and verified all 43 effects, plus their per-effectcustom*option bags. Magic Move correspondence is not persisted — it's a runtime heuristic, somagic-idmust be an authoring-time abstraction (confirmed from two angles).Phase 2 — object builds (Exps 5–11). Builds are a
KN.BuildArchive+KN.BuildChunkArchivepair referenced from the slide; order is list position, with no explicit order field. In/Out/Action are oneanimationTypeenum, Action carrying a distinct motion-path payload. Thecharactereffect suffix is not object-type-qualified. Builds also serializedirection— the same slot transitions omit at default.Backends. Transitions are fully AppleScript-scriptable (
deckkit.py+build_deck.py). Builds and direction are not scriptable, so they go through YAML archive surgery (archive_backend.py) on the repacked deck.The crash fix
Authored builds crashed Keynote 15.3 with
EXC_BREAKPOINT (SIGTRAP)in-[__NSSetM addObject:]. A four-deck bisect isolated it to build authoring, and comparison against human-authored fixtures found two missing document-level invariants inMetadata.iwa.yaml:KN.BuildArchiveid must be registered inTSP.PackageMetadata→ the slide's component →objectUuidMapEntries, with the entry'suuidequal to the chunk'sbuildId(8/8 human-authored builds register it; 0/2 of ours did).lastObjectIdentifiermust stay above every minted archive id (5/5 fixtures hold this; ours inverted it).Neither is visible in the decoded slide YAML, which is why structural verification passed while Keynote kept crashing.
_verify_uuid_mapnow enforces both from insidewrite_back.Verification
mise run test→ 18 tests, no Keynote required; both crash fixes mutation-testedmise run selftest,mise run verify-pack→ passKnown gaps
Build triggers other than
on_click(after_previous/with_previousneed fixtures), theapple:sidezoomalias reconciliation flagged inbuild_catalog.md, Exp 9's catalog rows not yet wired intodeckkit.py, the fulldirectionenum (two values known), anditem:Ntargets only. All tracked infindings/write_backend.mdandfindings/HANDOFF.md.Note on scope
This is the research prototype — Python tooling, findings docs, and fixtures.
findings/HANDOFF.mdis the entry point for picking the work back up.🤖 Generated with Claude Code